zoukankan      html  css  js  c++  java
  • JS简记-原型一

    js中的对象大多数都会有一个__proto__属性,该属性指向一个对象,当使用this查找属性时,就会沿着__proto_链查找,知道找到为止,否则返回undefined。

    下面首先来看一下最为基础的__proto__,也即Object的prototype(Object是一个函数对象,js中所有函数都会有一个prototype属性)。

    var o1= {
        a: 1
    };
    var o2 = new Object();
    o2.a = 2;

     以上代码在全局对象上添加了o1和o2两个属性,这两个属性分别指向两个对象,可以看到这两个对象的内容完全相同,并且都拥有一个__proto__。

    以o1为例来看__proto__对象:

    其中hasOwnProperty、isPrototypeOf、toString、valueOf都是对象比较常见的方法。

    __defineGetter__/__defineSetter__用来定义getter/setter,语法为obj.__defineGetter__(prop, func)/obj.__defineSetter__(prop, fun)。但是这两个方法已被废弃不推荐使用了,现在应该使用字面量或defineProperty来定义getter/setter,比如为对象o定义属性a的getter方法:

     1 var o = {
     2     get a(){
     3         return 2;
     4     }
     5 }
     6 //或者
     7 var o = Object.create(Object.prototype);
     8 Object.defineProperty(o, "a", {
     9     get: function(){
    10         return 2;
    11     }
    12 }); 

    __lookupGetter__/__lookupSetter用来查找某个属性的getter/setter方法,同样已被废弃不推荐使用,语法是obj.__lookupGetter__(sprop)/obj.__lookupSetter__(sprop),现在推荐使用Object.getOwnPropertyDescriptor:

    var o {
        get a(){
            return 1;
        }
    }
    var getterOfA = Object.getOwnPropertyDescriptor(o, "a").get;

    get __proto__/set __proto__看名字就知道其是用来访问__proto__属性的(大多数对象都会拥有一个__proto__属性,在存取该属性时,就会顺着__proto__链找到这两个方法并调用。使用Object.create(null)创建的对象没有__proto__,也就没有这两个方法,我们只能通过Object.setPrototypeOf为该对象增加__proto__,所以猜测这两个方法其实就是在调用Object.getPrototypeOf和Object.setPrototypeOf)。

     下面来进一步看一下__proto__。

     1 var o = {
     2     a: 1
     3 }
     4 o.__proto__ = true;//没效果,number/string/boolean/undefined全无效
     5 o.__proto__ = Object.create(null, {
     6     b: {
     7         value: 2
     8     }
     9 });//生效,说明__proto__的setter会判断接收到的值类型,如果是基础类型则忽略,只有对象类型才生效
    10 console.log(Object.getOwnPropertyDescriptor(o, "__proto__"));//undefined,说明__proto__并不属于o的属性
    11 console.log(Object.getOwnPropertyDescriptor(Object.prototype, "__proto__"));//正常输出,说明__proto__是Object.prototype的属性(默认以getter/setter形式存在)

    控制台输出如下,由于__proto__的setter的存在,o的__proto__确认改变了:

    由上分析可知,对于Object子对象来说,__proto__不属于其正常属性,但js引擎会顺着这个特殊的__proto__组成的链查找this.xxx,说明__proto__是引擎内部与对象其他属性独立的一个数据结构。
    为了进一步验证__proto__是引擎内部与对象其他属性独立的一个数据结构,进行以下实验

     1 efineProperty(Object.prototype, "__proto__", {
     2     value: Object.create(null),//原本应该为Object.getOwnPropertyDescriptor(Object.prototype, "__proto__").get()
     3     writable: true
     4 });//将Object.prototype中__proto__的getter/setter修改为value
     5 Object.prototype.c = 3;
     6 o = {
     7     a: 1
     8 }
     9 o.__proto__ = Object.create(null, {
    10     b: {
    11         value: 2
    12     }
    13 })//由于已经将Object.prototype中__proto__的getter/setter修改为value,所以这里对__proto__的设置不存在内部逻辑,仅仅是对属性的赋值
    14 console.log(Object.getOwnPropertyDescriptor(o, "__proto__"));//正常输出,说明已经设置了属性__proto__
    15 console.log(o.b);//undefined,说明js引擎并没有顺着属性__proto__查找属性d,如果顺着查找应该输出4。
    16 console.log(o.c);//3,说明存在属性__proto__的同时,js引擎还维护着一个特殊的__proto__,而该__proto__在对象创建时由js引擎通过Object.prototype提供。
    17 console.log(o);

    控制台输出,由于此时对__proto__的赋值操作相当于对普通属性赋值,所以o的特殊__proto__并没有改变(使用Object.setPrototypeOf可以改变),新增加的属性__proto__却没有显示,但通过Object.getOwnPropertyDescriptor发现其确实存在:

     可见__proto__并不是对象的一个普通属性,如果想修改一个对象的__proto__也不仅仅是修改该属性那么简单。之所以我们可以像修改属性那样修改__proto__,是因为Object.prototype中为__proto__提供了getter/setter,如果没有这对方法(比如var o = Object.create(null),o就没有这对方法),我们就需要使用Object.setPrototypeOf来设置__proto__了,此时如果仍旧使用普通属性赋值操作,则只会对普通属性__proto__赋值,而不是那个特殊的__proto__。

    注意与函数对象的prototype属性区分,prototype是函数对象的一个普通属性,Object的prototype不可配置且不可写(不可写意味着Object.prototype不可被赋其他值,但可以对其增加属性,Object.prototype.a=1),普通函数的prototype不可配置但可写。

  • 相关阅读:
    ubuntu lvm模式进行扩容
    Kickstart Round G 2018
    AtCoder Regular Contest 102 D
    论文阅读 | Clustrophile 2: Guided Visual Clustering Analysis
    Codeforces Round #504 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Final) E. Down or Right
    SQL语句报错:Incorrect string value: 'xE9x98xBFxE6x96xAF...'
    SQL语句报错:You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near
    WAMP集成环境虚拟路径修改
    L2-025 分而治之(图)
    L2-024 部落(并查集)
  • 原文地址:https://www.cnblogs.com/holoyong/p/8982829.html
Copyright © 2011-2022 走看看