1

Object.prototype上的属性及方法

  • hasOwnProperty()

    function Person(name) {
        this.name = name;
    }
    Person.prototype.sayName = function() {
        console.log(this.name);
    }
    var person = new Person('hx');
    person.name;
    // 'hx'
    person.hasOwnProperty('name');
    // true
    person.sayName();
    // hx
    person.hasOwnProperty('sayName');
    // false
  • isPrototypeOf()

    // 类似于instanceof
    function Animal() {}
    function Cat() {}
    Cat.prototype = Object.create(Animal.prototype);
    // Cat.prototype.__proto__ === Animal.prototype
    // Cat.prototype ---> Animal.prototype
    // 实现了原型继承,副作用是:Cat.prototype.constructor指向Animal,而不指向Cat;
    var cat = new Cat();
    Cat.prototype.isPrototypeOf(cat);
    // ture
    Animal.prototype.isPrototypeOf(cat);
    // ture
  • propertyIsEnumerable()

    function Person(name) {
        this.name = name;
    }
    Person.prototype.age = 18;
    var person = new Person('hx');
    console.log(person.name);
    // hx
    console.log(person.age);
    // 18
    person.propertyIsEnumerable('name');
    // true
    person.propertyIsEnumerable('age');
    // false

    Tips:需要特别注意!

    1. 属性是否可枚举,与属性是否在原型对象上没有一点关系;
    2. hasOwnProperty为true的属性不一定可枚举,反之亦然(等同于i);
    3. propertyIsEnumerable,其命名不太“严谨”,实际上它判断的是:① 不在原型对象上;② 且可枚举的属性;
    4. propertyIsEnumerable为false的属性不一定不可枚举,有可能是因为该属性在原型对象上(等同于iii)

Object对象自身的属性及方法

  • Object.assign()

    var obj1 = {a:1,b:2};
    var obj2 = {a:0,c:3};
    var newObj1 = Object.assign({},obj1,obj2);
    console.log(obj1); // {a:1,b:2}
    console.log(obj2); // {a:0,c:3}
    console.log(newObj1); // {a:0,b:2,c:3}
    var newObj2 = Object.assgin(obj1,obj2);
    console.log(obj1); // {a:0,b:2,c:3}
    console.log(obj2); // {a:0,c:3}
    console.log(newObj2); // {a:0,b:2,c:3}
    Tips:assign无法实现多维对象的深拷贝,不过可以实现一维对象的深拷贝
    var obj = {a:1,b:2};
    var newObj = Object.assign({},obj);
    console.log(obj,newObj); // {a:1,b:2} {a:1,b:2}
    newObj.a = 0, console.log(obj); // {a:1,b:2}
    obj.b = 100, console.log(newObj); // {a:0,b:2}
  • Object.create()

    // 使用指定的对象(做为新对象原型对象)和属性(可选)创建一个新对象实例
    function Person(name) {
        this.name = name;
    }
    Person.prototype.sayName = function() {
        console.log(this.name);
    }
    var a = Object.create(Person.prototype,{
        name:{
            writable:true,
            configurable:true,
            value:'hx'
        }
    })
    console.log(a); // {name:'hx'}
    a.sayName(); // 'hx'
    var Student = {
        name:'robot',
        height:1.2,
        run:function(){
            console.log(this.name+'is running')
        }
    }
    function createStudent(name) {
        var s = Object.create(Student);
        s.name = name;
        return s;
    }
    var xiaoming = createStudent('小明');
    xiaoming.run();
    // 小明is running
    xiaomi.__proto__ === Student
    Tips:实际上就是接受两个参数,第一参数是个对象,并赋给即将创建的对象的__proto__;第二个参数(可选)是一个自定义的属性。
  • Object.defineProperty()

    // 3个参数,为对象定义一个属性
    var obj = {name:'hx'};
    Object.defineProperty(obj,'age',{
        writable:true,
        configurable:true,
        value:18
    })
    console.log(obj); // {name:'hx',age:18}
    // 没有配置的enumerable默认为false
  • Object.defineProperties()

    // 2个参数,为对象定义多个属性
    var obj = {};
    Object.defineProperties(obj,{prop1:{
        value: 'hx',
        writable: true
    },prop2:{
        value:18,
        writable:true
    }})
    console.log(obj); // {prop1:'hx',prop2:18}
    // 没有配置的configurable,enumerable默认为false
    // 即:obj的属性prop1,prop2不可枚举,不可删除
  • Object.getOwnPropertyNames()

    var obj = {name:'hx',age:18};
    var names = Object.getOwnPropertyNames(obj);
    console.log(names); // ['name','age']
  • Object.getOwnPropertyDescriptor()

    var obj = {name:'hx',age:18};
    var prop = Object.getOwnPropertyDescriptor(obj,'age');
    console.log(prop); // {value:18,writable:true,enumerable:true,configurable:true} 
    Tips:上述的两个get方法都不会上原型链上去找
  • Object.getPrototypeOf()

    // 返回指定对象的原型对象(prototype)
    function Person(name) {
        this.name = name
    }
    Person.prototype.sayName = function() {
        console.log(this.name);
    }
    var a = Object.create(Person.prototype,{name:{
        value:'hx',
        writable:true,
        enumerable:true,
        configurable:true,
    }})
    Object.getPrototypeOf(a);
    // {sayname: f, construtor: f}
  • Object.setPrototypeOf()

    // 设置对象的原型
    // 示例 1
    var proto = {sayName:function(){
        console.log('hello world');
    }};
    var obj = {};
    Object.setPrototypeOf(obj,proto);
    obj.sayName(); // 'hello world'
    // 示例 2
    function Person(name) {
        this.name = name;
    }
    var person = new Person('hx');
    Object.setPrototypeOf(person,{sayAge:function(){
        console.log(18);
    }})
    console.log(person); // {name: 'hx'}
    person.sayAge(); // 18
    跟Object.new()有点类型,相当于直接给目标对象的隐式原型__proto__赋值(赋一个新对象),造成的影响是改变原型链(见文末)
  • Object.keys()

    // 返回一个包含所有给定对象自身可枚举属性名称的数组
    var obj = {name:'hx',age:18};
    console.log(Object.keys(obj));
    // ["name", "age"]
  • Object.values()

    // 返回一个包含所有给定对象自身可枚举属性名称的数组
    var obj = {name:'hx',age:18};
    console.log(Object.values(obj));
    // ["hx", 18]
  • Object.entries()

    var obj = {name:'hx',age:18};
    console.log(Object.entries(obj));
    // [["name", "hx"],["age", 18]]
  • Object.preventExtensions()

    // 禁止对象扩展(不可扩展,可修改,可删除)
    var obj = {name:'hx'};
    Object.preventExtensions(obj);
    obj.name = 'huangxin';
    obj.age = 18;
    console.log(obj.name,obj.age); // huangxin undefined
    delete obj.name; // true
    console.log(obj); // {}
  • Object.seal()

    // 阻止添加新属性并将所有现有属性标记为不可配置(不可删除,不可扩展,可修改)
    var obj = {name:'hx'};
    Object.seal(obj);
    obj.name = 'huangxin';
    obj.age = 18;
    console.log(obj.name,obj.age); // huangxin undefined
    delete obj.name; // false
    console.log(obj); // {name:'huangxin'}
  • Object.freeze()

    // 禁止对象变更(不可扩展,不可修改,不可删除)
    var obj = {name: 'hx'};
    Object.freeze(obj);
    obj.name = 'huangxin';
    obj.age = 18;
    console.log(obj.name,obj.age); // hx undefined
    delete obj.name; // false
    console.log(obj); // {name: 'hx'}
    Tips:
    执行完preventExtensions(),seal(),freeze()后,分别执行getOwnPropertyDescriptor():

    {value: "hx", writable: true, enumerable: true, configurable: true}

    {value: "hx", writable: true, enumerable: true, configurable: false}

    {value: "hx", writable: false, enumerable: true, configurable: false}

    可见:writable指可否更改,enumerable指可否枚举,configurable指可否删除,至于可否扩展,这里体现不出来,那么对象到底支不支持扩展就要用下面这个方法了
  • Object.isExtensible()

    var obj = {name: 'hx'};
    Object.isExtensible(obj); // true
    Obj.preventExtensions(obj);
    Object.isExtensible(obj); // false
    Tips:
    类似的对象属性判断还有 isFrozen(),isSealed()

写在最后

js的经典继承很大一部分体现在原型链的变化上,比如:

你想让Child继承Parent,只需:Child.prototype.__proto === Parent.prototype
这样当你在new一个Child实例的时候,child的原型对象上就有了parent的属性(实现了继承);
因此如何实现Child.prototype ---> Parent.prototype是原型继承的关键所在,其方法有:

  1. 通过new();

`
Child.prototype = new Parent();
`

  1. 通过Object.create();

`
Child.prototype = Object.create(Parent.prototype)
`

  1. 通过Object.setPrototypeOf();

`
Object.setPrototypeOf(Child.prototype,Parent.prototype)
`

以上三种方法都相当于改变了Child.prototype,不同的是对于Child.prototype来说,前两种属于赋值型改变,相当于把Child.prototype整体作为一个对象,做整体性赋值,结果导致了Parent.prototype.hasOwnProperty('constructor'); // false;而第三种属于扩展型改变,只是改变了Parent.prototype.__proto__

也就是说

  • 本来Child.prototype.constructor是指向Child自身的。
  • 当采用了方法1后,Child.prototype被重新赋值,失去了constructor属性(此时Child.prototype已经被赋值成一个Parent实例),其constructor属性只能从其原型对象上找,也即Parent.prototype.constructor,最后的结果就是Child.prototype.constructor指向Parent
  • 采用方法2也一样,Child.prototype被直接赋值成了一个空对象,Child.prototype.__proto__被赋值成了Parent.prototype,也便造成了同样的结果:Child.prototype.constructor指向Parent
  • 因此采用了方法1、2的话,还需要手动地把constructor指回来:Child.prototype.constructor = Child

对象赋值 vs. 对象扩展

  • 我们都知道:

    var obj1 = {name:'hx'};
    var obj2 = obj1;
    obj1 === obj2; // true
    
    // 对象扩展
    obj1.age = 18;
    obj1; // {name:'hx',age:18}
    obj1 === obj2; // true
    
    // 对象赋值
    obj1 = {name:'hx',age:18};
    obj1.name === obj2.name; // true
    obj1.age === obj2.age; // true
    obj1 === obj2; // false
  • 同样:

    var o1 = {};
    o1.__proto__ === Object.prototype; // true
    o1.__proto__.__proto__; // null
    
    // 对象扩展
    o1.__proto__.name = 'hx';
    o1.__proto__ === Object.prototype; // true
    o1.__proto__.__proto__; // null
    
    // 对象赋值
    o1.__proto__ = {name:'hx'};
    o1.__proto__ === Object.prototype; // false
    o1.__proto__.__proto__ === Object.prototype; // ture
    o1.__proto__.__proto__.__proto__; // null

结论
在实现js原型继承时,最好使用 ++Object.setPrototypeOf()++,相比于 ++new++ 和 ++Object.create++,其副作用更小,代码量也最小:

Object.setPrototypeOf(Swimmer.prototype, Man.prototype);

等同于:

Swimmer.prototype = Object.create(Man.prototype);
Swimmer.prototype.constructor = Swimmer;

時雨
91 声望6 粉丝

慢慢地把云笔记上的内容搬过来~