用自己的话记录下最近学到的几个知识点
一、原型与原型链
基础代码:
// 使用构造函数创建一个对象
function Person() {}
Person.prototype.name = 'Brand'
let person = new Person()
console.log(person.name) // Brand
prototype
每个函数都有一个 prototype
属性。
prototype
是函数才会有的属性。
函数的 prototype
属性是指通过调用构造函数而创建的对象实例的原型
,即上面代码中person
的原型。
proto
每一个JavaScript
对象(除了 null
)都具有的一个属性,叫__proto__
,这个属性会指向该对象的原型。
function Person() {}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
⚠️注意:__proto__
存在于 实例与构造函数的原型之间,而不是存在于实例与构造函数之间
可通过Object.getPrototypeOf()
获取对象的原型
Object.getPrototypeOf(person) === Person.prototype // true
constructor
constructor
是原型的一个属性,指向该原型所对应的构造函数,所有对象都会自动获得这个属性
Person.prototype.constructor === Person // true
读取实例的属性时,如果在实例中找不到,就会去找原型上的该属性,如果还找不到,会继续去原型的原型上查找,直到找到最顶层为止.
原型链
由相互关联的原型组成的链状结构就是原型链,即通过 __proto__ 把原型连起来的链状结构
例子:
function Person() {}
Person.prototype.name = 'Hello';
var person = new Person();
person.name = 'Hi';
console.log(person.name)
// 输出:Hi
// 因为在 person 上能找到 name 属性,值为 Hi
delete person.name; // 删除 person 的 name 属性
console.log(person.name)
// 此时,在 person 上找不到 name 属性
// 所以去 person 的原型上查找,即 person.__proto__ 上查找 name 属性
// 又因为 person.__proto__ === Person.prototype
// 所以 person.__proto__.name === Person.prototype.name
// 输出:Hello
Object.prototype
是最顶层原型,它的原型是 null
,即
Obkect.prototype.__proto__ === null
图中红线所组成的连接就叫原型链
二、词法作用域和动态作用域
JavaScript
采用的是词法作用域
,或者叫静态作用域
因为 js是采用的词法作用域
,所以函数的作用域在函数定义的时候就决定了
。
但在动态作用域
中,函数的作用域在函数调用的时候决定
。
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar(); // 1
上述代码:
- 如果 js 是动态作用域,则执行
bar()
后,应输出 2
; - 如果 js 是词法作用域,则执行
bar()
后,应输出 1
;
在浏览器中执行上面代码可以知道输出结果为 1
。验证了 js 是词法作用域。
再看两个例子:
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
输出结果都为:local scope
三、执行上下文
执行上下文
就是当前 JavaScript 代码被解析和执行时所在环境
的抽象概念
, JavaScript 中运行任何的代码都是在执行上下文中运行。
总共有三种类型:
- 全局执行上下文: 只有一个,浏览器中的全局对象就是
window
对象,this
指向这个全局对象。 - 函数执行上下文: 存在无数个,只有在函数被调用的时候才会被创建,每次调用函数都会创建一个新的执行上下文。
- Eval 函数执行上下文: 指的是运行在
eval
函数中的代码,很少用而且不建议使用。
执行栈
,也被叫做调用栈,具有 LIFO(后进先出)结构
。
当 JavaScript
引擎首次读取你的脚本时,它会创建一个全局执行上下文
并将其推入当前的执行栈。
执行栈的底部始终有个全局执行上下文。
每发生一个函数调用就会创建一个执行上下文,并推进执行栈中,执行完后,从栈顶删除。
例子:
let a = 'Hello World!';
function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
function second() {
console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');
js执行过程,可以看看我之前总结的这一篇文章:
http://www.brandhuang.com/article/1576067877012
执行上下文相关推荐文章:
先就记录这几个知识点吧,多了一次性也记不住,大概率你也不会来看第二遍