创建对象的几种方式

来源:互联网 发布:淘宝网2016全年交易额 编辑:程序博客网 时间:2024/06/05 02:56

在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
原创粉丝点击