创建对象的几种方式

在javascript中,所有对象的创建都是基于原型的。在js中任意的对象都有一个内部属性[[Prototype]]。这个属性的值只能是object或者是null。对象有这个内部属性的目的就是为了实现继承,或者更明确的说实现属性(方法)的复用。所以说创建一个对象的关键就是确定[[prototype]]的值。一般情况下,如果创建了一个对象什么属性也没有那么这个对象的原型就是Object.prototype。假设创建的对象的原型为null的话,则将不会继承toString等方法,也就不会进行相应的操作了。可以设么说,原型是js的核心。

 

创建一个对象有四种方式,分别是ES3之前的通过对象字面量创建、ES3中通过new创建对象、ES5标准下通过Object.create的方式创建对象、还有就是ES6中通过__proto__、Object.setPrototypeOf创建对象。其实上述这些方式根本是对对象的原型属性的操纵。

 

一、通过对象字面量创建对象

var myObj = {

    name: "maotr",

    "ID  code": 21

};

通过对象字面量窗帘对象中,创建对象表达式每执行一次,表达式每部的被赋值的表达式也会执行一次,所以会出现下面这个情况:

function f() {

    return myObj = {

        name: "maotr",

        eat: function () {}

    };

}

console.log(f() === f());//false

console.log(f().eat === f().eat);//false

所以说使用这种方式创建对象的话仅仅限于单例模式,对于其他的方式会很消耗内存的。

 

除了我们都知道的对象的属性除了可以是标识符之外,如果不符合标识符条件可以通过字符串的形式引用,比如:一开始那个例子。即使在ES5标准下,通过这种方式定义的对象的属性是不能变的。也就是说只能是标识符或者字符串常量。如果要使用变化的属性怎么办,只能通过如下方式:

function f(name, value) {

    var o = {

        x: 2,

        y: 4

    };

    o[name] = value;//使用[]访问运算符进行添加属性。

    return o;

}

但是在ES6标准下,可以这样了:

function f(name, value) {

    return {

        x: 2,

        y: 4,

        [name]: value

    };

}

console.log(f("z", 10).z);//10

也就是说,在ES6中:左边的属性名不再要求是常量了,而是可以使表达式。工作方式是:当执行到一个对象字面量表达式的时候,除了创建一个对象和计算每一个属性的值之外,还会计算:左边的方括号内的表达式,并且将结果通过调用内部的toString函数转化成字符串,也就是说如下实例:

function f(name, value) {

    return {

        x: 2,

        y: 4,

        [name+4]: value

    };

}

console.log(f("^", 10)["^4"]);//10

 

 

二、通过new关键字创建对象

New更像是为了能够靠近面向对象类式语言而引入的。New关键字实现的功能就是将一个对象的原型属性绑定到构造函数的prototype中去,然后执行构造函数执行初始化。所以这么看来函数中有prototype也是为了类式风格的面向对象设计而引入的。但是对于js本身而言,实现类或者继承使用原型的方式更加纯粹。

对于new关键字是如何工作的呢?看一下规范就明白了。

 

 

我们可以不是同new和函数内部的prototype情况下模拟类的创建。

function defineClass(initializer, proto) {
    if(typeof initializer !== 'function') {
        throw TypeError('the initializer may only an function');
    }
    return (function f() {
       var obj, result;
      if(!(proto instanceof Object)) {
        proto = Object.prototype;
      }else {
        proto.constructor = initializer;
      }
       //obj = Object.create(proto); ES5
       function Temp() {};
       Temp.prototype = proto;
       obj = new Temp();
       f.prototype = proto;
 
       result = initializer.apply(obj, arguments);
       if(result instanceof Object) {
            obj = result;
       }
 
       return obj;
    });
}
 
var Point = defineClass(function (x, y){
    this.x = x;
    this.y = y;
},{
    getLength: function(){
        let {x, y} = this;
        return Math.sqrt(x * x + y * y);
    }
 
});
 
var p = Point(3,4);
 console.log(p.getLength(),p instanceof Point, p instanceof Object);//5 true true


所以这种通过new创建对象的方式使得创建对象时要创建一个函数作为构造函数,使得原型这个工具使用起来比较局限,不能灵活的去使用。下面介绍的Object.create略有改善。

 

三、通过Object.create创建对象

Object.create函数传入第一个参数是返回的对象的原型,第二个参数是添加到对象中的属性的特性的描述。但是对于IE6到8来说存在兼容性的问题。所以要处理兼容性:

if (typeof Object.create !== 'function') {
    Object.create = (function () {
        //为了省内存,共享一个构造器
        function Temp() {};
 
        //使用Object.propertype.hasOwnProperty更安全的引用
        var hasOwn = Object.prototype.hasOwnProperty;
 
        return function (proto, properties) {
            //1、如果proto不是Object或者null,则抛出异常
            if(typeof proto !== 'object') {
                throw TypeError('Object prototype may only be an Object or null');
            }
 
            //2创建一个对象obj,和通过new Object()方式创建的一样
            //3设置obj的prototype值为proto
            Temp.prototype = proto;
            var obj = new Temp();
            Temp.prototype = null;//不要保持一个proto的杂散引用
 
            //4如果存在参数properties 而不是undefined
            //那么就把参数的自身属性添加到obj上,就像是调用
            //携带obj,properties两个参数的标准内置函数
            //Object.properties()一样
            if (properties !== undefined) {
                for (var key in properties) {
                    if(hasOwn.call(properties, key)) {
                        obj[key] = properties[key];
                    }
                }
            }
            //5返回obj
            return obj;
        }
    })();
}


 

ES5这个方法的提出,使得创建对象可以直接明确的赋予原型值,使得原型成为一个可以控制的量,真正的将js原型的功能发挥出来,但是有一个缺点就是一旦创建了这个对象之后,这个对象就和这个传入的原型对象绑定在一起,不够灵活。下面ES6能够直接操作对象的原型属性,所以更加灵活了。

 

四、通过Object.setPrototypeOf创建对象。

 

Object.setPrototypeOf方法,用来设置一个对象的prototype对象,返回参数对象本身。

 

let proto = {};let obj = { x: 10 };

Object.setPrototypeOf(obj, proto);

 

proto.y = 20;

proto.z = 40;

 

obj.x // 10

obj.y // 20

obj.z // 40


注意返回的是设置对象本身,所以:

如果第一个参数不是对象,会自动转为对象。但是由于返回的还是第一个参数,所以这个操作不会产生任何效果。

Object.setPrototypeOf(1, {}) === 1 // true

Object.setPrototypeOf('foo', {}) === 'foo' // true

Object.setPrototypeOf(true, {}) === true // true


由于undefined和null无法转为对象,所以如果第一个参数是undefined或null,就会报错。

Object.setPrototypeOf(undefined, {})

// TypeError: Object.setPrototypeOf called on null or undefined

Object.setPrototypeOf(null, {})

// TypeError: Object.setPrototypeOf called on null or undefined

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值