摘要:创建对象的多种方式以及优缺点
创建
字面量模式
在计算机科学中,字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。字面量表示的优点就是对象的属性和值对应的方式一目了然
当需要创建大量相似对象的时候,字面量模式就不适合了,因为它需要创建多个对象,会有大量的重复代码
1 | var person = { |
操作对象属性时,可以用.
也可以用[]
。但有些区别:中括号运算符总是能代替点运算符,但点运算符却不一定能全部代替中括号运算符;中括号运算符可以用纯数字、字符串变量的内容、js 的关键字和保留字作为属性名,点运算符不能。使用中括号运算符时字符串记得用引号包裹
1 | person.name; // xiaowang |
工厂模式
说白了就是封装,将一段重用性高的代码封装起来,以便多次调用。缺点是实例对象的类型无法区分,因为封装后外界不知道是哪个构造函数实例化的,instanceof 判断不出来
1 | function Person(name) { |
构造函数模式
构造函数的原理与工厂模式一样:先创建一个对象,再给对象添加上属性和方法。每次创建实例时,每个方法都要被创建一次。
构造函数与普通函数的区别:
1.构造函数的首字母大写(约定俗成,小写也没问题)
2.构造函数使用 new 操作符
1 | function Person(name) { |
优化一下,将函数定义转移到构造函数外部:
1 | function Person(name){ |
这样所有的实例对象就共享了全局作用域中定义的同一个 say 函数,但是当对象需要很多方法时,就要定义很多个全局函数,这样毫无封装性
原型模式
使用构造函数创建实例,每次创建实例都要重新创建一次相同的方法,这是完全没有必要的,而原型模式可以将公共的属性和方法提出来。但要注意的是:原型模式下如果原型的属性被一个实例修改,因为所有实例共享同一个原型对象,那么所有实例引用的原型属性都将被修改,但是每个实例可以添加自己的属性、方法,该属性会覆盖原型对象上的同名属性和方法。
1 | function Person() { } |
混合模式(原型模式 + 构造函数模式)
混合模式中构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。每个实例都会有自己的私有属性,同时又共享者相同方法的引用,最大限度的节省了内存,但是对于代码的封装性不够好。
1 | function Person(name) { |
动态原型模式
通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型,可以实现仅在第一次调用函数时对原型对象赋值一次的效果。这一种方式很好地对混合模式进行了封装:
1 | function Person(name) { |
注意:使用动态原型模式时,不能用对象字面量重写原型
1 | function Person(name) { |
使用 new 新建一个实例 person1,假如此时 Person.prototype 指向 A,那么 person1 的实例原型 person1.__proto__ 也是指向 A。检测到 A 没有 say 方法,Person.prototype 被修改由指向 A 改为指向 B。但是 person1.__proto__ 的指向并没有发生改变,还是指向 A,所以 person1 调用 say 方法时会报错
寄生构造函数模式
从代码上看寄生构造函数模式就是比工厂模式多使用了一个 new,构造函数在不返回值的情况下会默认返回新的对象实例,但使用 return 可以重写返回的值,重写后构造函数返回的对象与构造函数或者构造函数的原型属性之间没有任何关系。在可以使用其他模式的情况下,不要使用这种模式
1 | function Person(name) { |
稳妥构造函数模式
所谓稳妥对象,指的是没有公共属性,而且其方法也不引用 this 的对象,稳妥对象最适合在一些安全的环境中。稳妥构造函数模式也无法识别对象所属类型
稳妥构造函数模式与寄生构造函数模式有两点不同:
1.创建实例方法不引用 this
2.不使用 new 操作符调用构造函数
1 | function Person(name) { |