JavaScript 创建对象的九种方式
ES5 创建对象(八种)
直接对象字面量
const obj = { name: 'dz', age: 23 }
这种方式死板, 灵活度不高, 每次都需要手动创建, 冗余度也很大, 适用于临时对象变量或者局部属性少的对象
工厂模式
function Person() { var o = new Object(); o.name = 'hanmeimei'; o.say = function() { alert(this.name); } return o;}var person1 = Person();
优点: 完成了返回一个对象的要求。
缺点:
- 无法通过constructor识别对象,以为都是来自Object,无法得知来自Person
- 每次通过Person创建对象的时候,所有的say方法都是一样的,但是却存储了多次,浪费资源。
构造函数模式
function Person() { this.name = 'hanmeimei'; this.say = function() { alert(this.name) }}var person1 = new Person();
优点:
- 通过constructor或者instanceof可以识别对象实例的类别
- 可以通过new 关键字来创建对象实例,更像OO语言中创建对象实例
缺点:
- 多个实例的say方法都是实现一样的效果,但是却存储了很多次(两个对象实例的say方法是不同的,因为存放的地址不同)
注意:
- 构造函数模式隐试的在最后返回
return this
所以在缺少new的情况下,会将属性和方法添加给全局对象,浏览器端就会添加给window对象。 - 也可以根据
return this
的特性调用call或者apply指定this。这一点在后面的继承有很大帮助。
原型模式
function Person() {}Person.prototype.name = 'hanmeimei';Person.prototype.say = function() { alert(this.name);}Person.prototype.friends = ['lilei'];var person1 = new Person();
优点:
-
say方法是共享的了,所有的实例的say方法都指向同一个。
-
可以动态的添加原型对象的方法和属性,并直接反映在对象实例上。
var person1 = new Person()Person.prototype.showFriends = function() { console.log(this.friends)}person1.showFriends() //['lilei']
缺点:
-
出现引用的情况下会出现问题具体见下面代码:
var person1 = new Person();var person2 = new Person();person1.friends.push('xiaoming');console.log(person2.friends) //['lilei', 'xiaoming']
因为js对引用类型的赋值都是将地址存储在变量中,所以person1和person2的friends属性指向的是同一块存储区域。
-
第一次调用say方法或者name属性的时候会搜索两次,第一次是在实例上寻找say方法,没有找到就去原型对象(Person.prototype)上找say方法,找到后就会在实力上添加这些方法or属性。
-
所有的方法都是共享的,没有办法创建实例自己的属性和方法,也没有办法像构造函数那样传递参数。
注意:
-
优点②中存在一个问题就是直接通过对象字面量给
Person.prototype
进行赋值的时候会导致constructor
改变,所以需要手动设置,其次就是通过对象字面量给Person.prototype
进行赋值,会无法作用在之前创建的对象实例上var person1 = new Person()Person.prototype = { name: 'hanmeimei2', setName: function(name){ this.name = name }}person1.setName() //Uncaught TypeError: person1.set is not a function(…)
这是因为对象实例和对象原型直接是通过一个指针链接的,这个指针是一个内部属性[[Prototype]],可以通过
__proto__
访问。我们通过对象字面量修改了Person.prototype指向的地址,然而对象实例的__proto__
,并没有跟着一起更新,所以这就导致,实例还访问着原来的Person.prototype
,所以建议不要通过这种方式去改变Person.prototype
属性
构造函数和原型组合模式
function Person(name) { this.name = name this.friends = ['lilei']}Person.prototype.say = function() { console.log(this.name)}var person1 = new Person('hanmeimei')person1.say() //hanmeimei
优点:
- 解决了原型模式对于引用对象的缺点
- 解决了原型模式没有办法传递参数的缺点
- 解决了构造函数模式不能共享方法的缺点
缺点:
- 和原型模式中注意①一样
动态原型模式
function Person(name) { this.name = name if(typeof this.say != 'function') { Person.prototype.say = function( alert(this.name) }}
优点:
- 可以在初次调用构造函数的时候就完成原型对象的修改
- 修改能体现在所有的实例中
缺点: 红宝书都说这个方案完美了。。。。
注意:
- 只用检查一个在执行后应该存在的方法或者属性就行了
- 不能用对象字面量修改原型对象
寄生构造函数模式
function Person(name) { var o = new Object() o.name = name o.say = function() { alert(this.name) } return o}var peron1 = new Person('hanmeimei')
优点:
- 和工厂模式基本一样,除了多了个new操作符
缺点:
- 和工厂模式一样,不能区分实例的类别
稳妥构造模式
function Person(name) { var o = new Object() o.say = function() { alert(name) }}var person1 = new Person('hanmeimei');person1.name // undefinedperson1.say() //hanmeimei
优点:
- 安全,那么好像成为了私有变量,只能通过say方法去访问
缺点:
- 不能区分实例的类别
ES6 创建对象(一种)
class Person {
constructor(name, age) { // constructor构造函数
this.name = name
this.age = age
}
sayname() { //原型上的
console.log(this.name)
}
static sayAge() {
console.log(this.age)
}
}
const per = new Person('dz', 23)
per.sayname() // -> dz
Person.sayAge() // 23
constructor
是构造方法,类似构造函数, 定义这个方法里面的内容都是实例自身的属性和方法, 不会被其他实例共享, 而写在外面的sayname
表示原型上的方法, 是会被共享
的.
static
表示静态,加了static的函数不会
挂载到prototype
上,而是挂载到 class类
上, 类似于:
Promise.resolve( )
Math.max( )
\