JavaScript中的链(作用域链、原型链)

JavaScript中的链(作用域链、原型链)

Js中存在两种链,作用域链和原型链。作用域链是为了访问变量而存在的链,原型链是访问对象的属性而存在的链。

作用域链

说到作用域链,首先来说下作用域的概念:执行代码的上下文,也可以说是变量对象,是开始进入一个函数的执行环境的时候,形成的所有可访问变量,可以说变量对象是作用域的实体。

作用域分类: 全局作用域、函数作用域(块级作用域)、eval作用域。ES6之前的块级作用域只指函数作用域,ES6以后,if,while,switch,for语句都可以形成自己的块级作用域(使用非var来声明变量)。全局作用域是作用域链的最顶层。

作用域链的概念: 是由作用域组成的一个带头结点的单向链表,主要作用域是用来查找变量的。【scope】属性是一个指向这个链表头结点的指针。

具体来说形成过程: 在定义一个函数的时候,在函数内部会创建一个scope属性,这个属性指向一个作用域链。在确定其scope属性的时候,js解析器按照一定的规则来确认作用域链:从函数内部向外遍历,每当遇到一个function的时候,就把这个function的变量对象添加到作用域链上,一直到最顶层window对象。最后再把作用域链的引用赋给scope属性。

例:

function Outer(){
    var outer = 'outer';
    Inner();
    function Inner(){
        var inner = 'inner';
        console.log(outer,inner) // outer inner
    }
}

当引擎执行到函数Inner内部的时候,不仅可以访问当前作用域而且可以访问到Outer的作用域,从而可以访问到标识符outer。因此发现当多个作用域相互嵌套的时候,就形成了作用域链。词法作用域在查找标识符的时候,优先在本作用域中查找。如果在本作用域没有找到标识符,会继续向上一级查找,当抵达最外层的全局作用域仍然没有找到,则会停止对标识符的搜索。
  
  如果没有查找到标识符,会根据不同的查找方式作出不同的反应。如果是RHS,则会抛出Uncaught ReferenceError的错误,如果是LHS,则会在查找最外层的作用域声明该变量,这就解释了为什么对未声明的变量赋值后该变量会成为全局变量。
  
  所以上面的代码执行console.log(outer,inner)的时候,引擎会首先要求Inner函数的词法作用域查找(RHS)标识符outer,被告知该词法作用域不存在该标识符,然后引擎会要求嵌套的上一级Outer词法作用域查找(RHS)标识符outer,Outer词法作用域的查找成功并将结果返回给引擎。

原型链

说到原型链,首先说下原型,原型就是原型对象,普通对象(不是通过new Function创建的对象)没有原型对象,只有函数对象才有原型对象。

原型链是通过__proto__属性形成的,任何对象(普通对象和函数对象)都有__proto__属性。查找对象的某个属性的时候,首先在当前对象查找,如果没有去对象的__proto__中去查找,一直到最顶层null,这样形成的一条查找链就是原型链。

  • 所有对象的 __proto__都指向他的构造函数的prototype
  • 所有函数对象(Number,String,Date,Object等12种内置构造函数,还包括自定义构造函数)的 __proto__都指向Function.prototype(他是一个空函数empty function)

JavaScript中"类"在实例化的时候,只是给了实例一个 __proto__属性让JavaScript实例对象通过,这个属性找到类定义的属性和方法。从而可以操作和调用“类”的属性和方法。

function Person(){}
var person1 = new Person();
person1.valueOf();

以访问person1的valueOf为例:

  • 浏览器首先检查,person1 对象是否具有可用的 valueOf() 方法。
  • 如果没有,则浏览器检查 person1 对象的原型对象(即person1.__proto__指向的对象也是 Person构造函数的prototype属性所指向的对象)是否具有可用的 valueof() 方法。
  • 如果也没有,则浏览器检查 Person() 构造函数的prototype属性所指向的对象的原型对象(即person1.proto.__proto__指向的对象也就是 Object构造函数的prototype属性所指向的对象)是否具有可用的 valueOf() 方法。这里有这个方法,于是该方法被调用。
  • 如果还是没有。则会到Object构造函数的prototype属性所指向的对象的原型对象也就是null,没得找所以就是undefined。

1、“类”的__proto__等于其父类的prototype

var foo=function(){};
alert(foo.__proto__==Function.prototype)  //返回true

2、“类”实例的__proto__等于该“类”的prototype

var foo=function(){};
var f=new foo();
alert(f.__proto__==foo.prototype)  //返回true

3、“类”的Prototype是一个独立的对象。该对象默认是一个Object对象的实例,所以“类”的prototype属性的__proto__属性的值默认等于Object.prototype

	
var foo=function(){};
alert(foo.prototype.__proto__==Object.prototype)  //返回true

4、因为在JS中Object其实也是Function,所以alert(Object.proto==Function.prototype);返回true

5、特殊情况

 Object.prototype.__proto__==null//为了防止原型链无限循环的发生

由此可得:Function.prototype.__proto__.__proto__==null

小结: __ proto__存在的作用——当调用一个“类”实例的某个属性/方法,如果这个属性/方法在当前“类”中未定义(也就是在“类”实例本身没有找到)则会在__proto__中去查找这个属性/方法,如果__proto__中不存在这个属性/方法,继续搜索__proto__.__proto__中去查找,直到找到这个属性/方法或者__proto__等于null时

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值