原型链与声明提升(预解析)

原型链

显示原型与隐式原型

显示原型prototype

隐式原型[[prototype]] (__proto__已被弃用,现在用[[prototype]]代替)

原型链

当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的[[prototype]]隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的[[prototype]]中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。

注意: __proto__已被[[prototype]]代替,[[prototype]]通过Object.getPrototypeOf(实例对象)获取

image

image

实例对象.__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

声明提升(预解析)及其优先级

函数的形参会覆盖预解析的变量

var foo={n:1};
(function(foo){
	console.log(foo.n);
	foo.n=3;
	var foo={n:2};
	console.log(foo.n);
})(foo);
console.log(foo.n)

//预解析
var foo={n:1};			//foo=0x100
(function(foo){
	var foo;			//声明变量foo
	var foo=foo;		//声明形参foo=实参foo=0x100;
	console.log(foo.n);	//0x100.n=1;
	foo.n=3;			//0x100.n=3;
	foo={n:2};			//形参foo=0x200;
	console.log(foo.n);	//0x200.n=2;
})(foo);
console.log(foo.n)		//ox100.n=3;

预解析做了什么?

执行上下文主要干了如下两件事:

  1. 创建执行环境
    • 收集变量形成一个变量对象 (此时作用域内使用该变量的都已经被赋值,包括调用该变量当实参) --> 预解析
    • 确定this指向
    • 确定当前执行环境的作用域
  2. 执行执行环境中代码

函数的声明和变量的声明都会提升到其所在作用域最顶端(函数体内提升到函数体最顶部,全局提升到全局最顶部)

函数首先被提升,然后才是变量。(函数是一等公民,优先编译)

函数提升优先级比变量提升要高,且不会被变量声明覆盖,但是会被变量赋值覆盖。

典型例子:

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);
posted @ 2022-03-25 20:08  听风小弟  阅读(38)  评论(0)    收藏  举报