原型链与声明提升(预解析)
原型链
显示原型与隐式原型
显示原型prototype
隐式原型[[prototype]]
(__proto__
已被弃用,现在用[[prototype]]代替)
原型链
当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的[[prototype]]
隐式原型上查找,即它的构造函数的prototype
,如果还没有找到就会再在构造函数的prototype的[[prototype]]
中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。
注意: __proto__
已被[[prototype]]
代替,[[prototype]]
通过Object.getPrototypeOf(实例对象)
获取
实例对象.__proto__===Object.getPrototypeOf(实例对象)
构造函数的显示原型(prototype
)等于实例对象的隐式原型([[prototype]]
)
构造函数.prototype===实例对象.__proto__
所有构造函数都是Function的实例,包括Function自身,
构造函数.__proto__===Function.prototype
所有显式原型都是Object的实例,包括Object自身
prototype.__proto__===Object.prototype
Function因为是自身的实例,因此Function既可以通过prototype
访问自身的原型对象,也可以通过__proto__
访问自身的原型对象
Function.__proto__===Function.prototype
Function.__proto__.__proto__===Function.prototype.__proto__
Object的原型对象的__proto__
指向null
(原型链的终点)
因为如果Object.prototype===Object.prototype.__proto__
(所有原型对象都是Object的实例)原型链最终将一直访问Object.prototype
没有尽头,因此设定Object.prototype.__proto__===null
Object.prototype.__proto__===null
声明提升(预解析)及其优先级
函数的声明和变量的声明都会提升到其所在作用域最顶端(函数体内提升到函数体最顶部,全局提升到全局最顶部)
函数首先被提升,然后才是变量。(函数是一等公民,优先编译)
函数提升优先级比变量提升要高,且不会被变量声明覆盖,但是会被变量赋值覆盖。
典型例子:
console.log(foo);
foo();
var foo = "变量";
function foo(){
if(false){
var foo=1;
}
console.log(foo);
}
console.log(foo);
代码预解析为:
function foo(){
var foo;
if(false){
foo=1;
}
console.log(foo)
}
var foo;
foo(); //控制台打印undefiend
console.log(foo); //foo函数体
foo = "变量";
console.log(foo); //变量
注意:
1.函数表达式声明时,只有前面的变量会发生声明提升,后面的函数并不会发生声明提升
2.变量只赋值不声明js引擎会自动在全局中声明(但是只在这个变量赋值之后的位置有效)严格模式下会报错
函数表达式声明例子
console.log(fn); //undefined
var fn=function(){
}
//预解析为:
var fn;
console.log(fn);
fn=function(){
}
变量值赋值不声明例子
b=10;
console.log(b); //10
console.log(a); //Error:a is not defined
a=10;
变量值赋值不声明例子之a=b=10
a=b=10;
(function(){
var a=b=20;
})();
console.log(a,b); //10,20
//预解析
a=10;
b=10;
(function(){
var a=20;
b=20;
})
console.log(a,b);