美文网首页
JavaScript的对象和原型链

JavaScript的对象和原型链

作者: 司超 | 来源:发表于2019-03-30 09:08 被阅读0次

    首先了解一下创建对象的几种方式,介绍以下三种。

    // 第一种方式:字面量
    var o1 = {name: 'o1'};
    var o2 = new Object({name: 'o2'});
    
    // 第二种方式:构造函数
    var M = function (name) { this.name = name; }
    var o3 = new M('o3');
    
    // 第三种方式:Object.create 
    var p = {name: 'p'};
    var o4 = Object.create(p);
    
    原型链1.png

    在上面的例子中o3就是实例,M就是构造函数。从上图中可以知道,实例的__protpo__指向的是构造函数的原型对象。每个对象都有 __proto__ 属性,但只有函数对象才有prototype属性。

    使用Object.create()创建关联

    var foo = {
        something: function() {
            console.log('ok');
        }
    };
    
    var bar = Object.create( foo );
    bar.something(); // ok
    console.log(bar.__proto__ === foo) // true
    

    Object.create()是ES5新增的函数。Object.create()方法接受两个参数:Object.create(obj,propertiesObject) 。
    obj:一个对象,是新创建的对象的原型。
    propertiesObject:该参数对象是一组属性与值,该对象的属性名称将是新创建的对象的属性名称,可选。

    var o = Object.create(Object.prototype, {
    // foo会成为所创建对象的数据属性
    foo: { 
        writable:true,
        configurable:true,
        value: "hello" 
    }}),
    
    console.log(o);//{foo:'hello'}
    

    Object.create(null) 创建的对象是一个空对象,在该对象上没有继承 Object.prototype 原型链上的属性或者方法,例如:toString(), hasOwnProperty()等方法。Object.create(null) 适合用来存储字典数据。

    var test1 = Object.create(null) ;
    console.log(test1);// {} No Properties 
    

    Object.create()的polyfill代码

    if(!Object.create) {
        Object.create = function(o) {
            function F() {}
            F.prototype = o;
            return new F();
        }
    }
    

    什么是原型链?

    简单理解就是原型组成的链,实例的__proto__是构造函数的原型,而原型也是一个对象,也有__proto__属性,就这样可以一直通过__proto__向上找,这就是原型链,当最终找到Object的原型的时候,这条原型链就算到头了。

    function Parent() {}
    
    function Child() {}
    Child.prototype = new Parent();
    
    var c = new Child();
    
    console.log(c.__proto__ === Child.prototype)   // true
    console.log(Child.prototype.__proto__ === Parent.prototype)   // true
    console.log(Parent.prototype.__proto__ === Object.prototype)  // true
    console.log(Object.prototype.__proto__)  // null
    

    使用for...in遍历对象时原理和原型链类似,任何可以通过原型链访问到(enumerable)的属性都会被枚举。

    var anotherObj = { a:2 };
    // 创建一个关联到anotherObj的对象
    var myObj = Object.create(anotherObj);
    for(var k in myObj) {
        console.log("found: " + k);
    }
    // found: a
    ("a" in myObj); // true
    
    instanceof原理.png

    instanceof是判断实例对象的_proto__和生成该实例的构造函数的prototype是不是引用的同一个地址,是返回true,否返回false。
    注意:实例的instanceof在比较的时候,与原型链上的构造函数相比都是true。

    var M = function (name) { this.name = name; }
    var o3 = new M('o3')
    var o5 = new M()
    o3.__proto__.say = function(){
       console.log('hello world')
    }
    
    o3.say()
    o5.say()
    

    只有函数有prototype,对象是没有的。但是函数也是有__proto__的,因为函数也是对象。函数的__proto__指向的是Function.prototype。也就是说普通函数是Function这个构造函数的一个实例。

    constructor

    function Foo() { /*  .. */ }
    Foo.prototype = { /* .. */ } // 创建一个新原型对象
    
    var a1 = new Foo();
    a1.constructor === Foo; // false!
    a1.constructor === Object; // true!
    

    Foo.prototype的constructor属性只是Foo函数在声明时的默认属性。如果创建一个新对象并替换函数默认的.prototype对象引用,那么新对象并不会自动获得.constructor属性。

    属性设置和屏蔽

    myObj.foo = 'bar';
    

    如果myObj中包含为foo的普通数据访问属性,这条赋值语句只会修改已有的属性值。如果foo不是直接存在于myObj中,原型链就会被遍历,类似[[Get]]操作。如果原型链上找不到foo,foo就会被直接添加到myObj上。

    如果foo既出现在myObj中也出现在原型链上层,则会发生屏蔽。myObj中的foo属性会屏蔽原型链上层的所有foo属性,因为myObj.foo总会选择原型链中最底层的foo属性。

    如果foo不直接存在于myObj中而存在于原型链上层时,会出现下面三种情况:

    1. 如果原型链上层存在名为foo的普通数据访问属性并且没有被标记为只读(writable: false),那就会直接在myObj上添加foo属性,它是屏蔽属性。
    2. 如果原型链上层存在名为foo的普通数据访问属性并且被标记为只读(writable: false),那么无法修改已有属性或创建屏蔽属性。严格模式下会报错。
    3. 如果原型链上层存在foo并且它是一个setter,那会调用这个setter,foo不会被添加到myObj,也不会重新定义foo。(注:与实际代码不符)

    例2

    var o = Object.create(Object.prototype, {
      foo: { 
        writable:false,
        configurable:true,
        value: "hello" 
    }});
    
    var o2 = Object.create(o);
    
    o2.foo = 123;  // 无效
    
    console.log(o2); // {}
    

    例3

    var obj2 = {
           val:200
    };
    obj2.__defineGetter__('name',function(){return this.val});
    obj2.__defineSetter__('name',function(name){this.val = name;});
    
    var myObj = Object.create(obj2);
    myObj.name = '999';
    // {val: "999"}
        name: "999"
        val: "999"
        __proto__:
                name: "999"
                val: 200
                get name: ƒ ()
                set name: ƒ (name)
                __proto__: Object
    

    相关文章

      网友评论

          本文标题:JavaScript的对象和原型链

          本文链接:https://www.haomeiwen.com/subject/bctjbqtx.html