建立对象(学习笔记) —— 《高级教程》

建立单个对象的缺点:用同一个接口建立不少对象,会产生大量的重复代码。javascript

工厂模式就是为了解决这个问题。java

工厂模式

解决了建立多个类似对象的问题es6

function createPerson(name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function() { console.log(this.name) } return o; } var person1 = createPerson('Mike', 28, 'xxx'); console.log(person1); person1.sayName(); var person2 = createPerson('Mike', 24, 'aaa'); console.log(person2); person2.sayName();

 

缺点:没法解决对象识别的问题——怎样知道一个对象的类型浏览器

构造函数模式

ECMAScript中的构造函数能够用来建立特定类型的对象。安全

function Person(name, age , job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { console.log(this.name); } } var person1 = new Person('Mike', 28, 'teacher'); console.log(person1); person1.sayName(); var person2 = new Person('Danie', 24, 'doctor'); console.log(person2); person2.sayName();

与工厂模式的区别:函数

  • 没有显示的建立对象
  • 将属性和方法赋值给了this对象
  • 没有return语句

构造函数自己也是函数,只不过能够用来建立对象this

用new操做符新建构造函数的实例,经历4个步骤:spa

person1 和 person2 分别保存着 Person 的两个不一样实例,都有一个 constructor (构造函数) 属性,指向 Personprototype

console.log(person1.constructor == Person); // true
console.log(person2.constructor == Person); // true
console.log(person1.constructor == Object); // false
console.log(person2.constructor == Object); // false

person1 和 person2 都是 Person 的实例,也是 Object 的实例,能够经过 instanceof 操做符来检验。code

console.log(person1 instanceof Person); // true
console.log(person2 instanceof Person); // true
console.log(person1 instanceof Object); // true
console.log(person2 instanceof Object); // true

缺点:每一个构造函数中的方法都要在新的实例上建立一遍。

console.log(person1.sayName == person2.sayName); // false

将相同的方法移到外部:

function sayName() { // 将相同的方法移到构造函数的外部
    console.log(this.name); } function Person(name, age , job) { this.name = name; this.age = age; this.job = job; this.sayName = sayName; } var person1 = new Person('Mike', 28, 'teacher'); console.log(person1); person1.sayName(); var person2 = new Person('Danie', 24, 'doctor'); console.log(person2); person2.sayName();
// 此时不一样实例上的方法就相等了
console.log(person1.sayName == person2.sayName); // true

缺点:须要在全局做用域定义不少函数,没有封装性可言

原型模式

好处: 全部对象实例可共享它所包含的属性和方法

function Person() { } Person.prototype.name = 'Mike'; Person.prototype.age = 28; Person.prototype.job = 'teacher'; Person.prototype.sayName = function() { console.log(this.name); } var person1 = new Person(); var person2 = new Person(); person1.sayName(); person2.sayName(); console.log(person1.sayName == person2.sayName); // true

下图以上面代码为例,展现了Person构造函数、Person的原型属性,及两个实例之间的关系。

 

在实现中,没法访问 [[Prototype]],能够用 isPrototypeOf() 方法来肯定对象之间是否有这种关系。

console.log(Person.prototype.isPrototypeOf(person1)); // true
console.log(Person.prototype.isPrototypeOf(person2)); // true

经过 Object.getPrototypeOf() 方法,访问原型对象上的属性

console.log(Object.getPrototypeOf(person1) == Person.prototype); // true
console.log(Object.getPrototypeOf(person1).name); // Mike

修改实例属性 

// 原型模式 修改实例属性

function Person() { } Person.prototype.name = 'Mike'; Person.prototype.age = 28; Person.prototype.job = 'teacher'; Person.prototype.sayName = function() { console.log(this.name); } var person1 = new Person(); person1.name = 'Gray'; person1.job = 'doctor'; var person2 = new Person(); person1.sayName(); // Gray
person2.sayName(); // Mike
 console.log(person1); // Person {name: "Gray", job: "doctor", __proto__: Object}
console.log(person2); // Person {__proto__: Object}

修改实例属性为 null, 不会恢复指向原型的连接

function Person() { } Person.prototype.name = 'Mike'; Person.prototype.age = 28; Person.prototype.job = 'teacher'; Person.prototype.sayName = function() { console.log(this.name); } var person1 = new Person(); person1.name = 'Gray'; var person2 = new Person(); person1.sayName(); // Gray
person2.sayName(); // Mike
person1.name = null; // 不会恢复指向原型的连接
person1.sayName(); // null

删除实例属性 会从新恢复指向原型对象的连接

// 原型模式 删除实例属性 会从新恢复指向原型对象的连接

function Person() { } Person.prototype.name = 'Mike'; Person.prototype.age = 28; Person.prototype.job = 'teacher'; Person.prototype.sayName = function() { console.log(this.name); } var person1 = new Person(); person1.name = 'Gray'; var person2 = new Person(); person1.sayName(); // Gray
person2.sayName(); // Mike
delete person1.name; // 会从新恢复指向原型对象的连接
person1.sayName(); // Mike

hasOwnProperty() 检测一个属性存在于实例中仍是存在于原型中。

// 原型模式 hasOwnProperty()

function Person() { } Person.prototype.name = 'Mike'; Person.prototype.age = 28; Person.prototype.job = 'teacher'; Person.prototype.sayName = function() { console.log(this.name); } var person1 = new Person(); console.log(person1.hasOwnProperty('name')); // false
person1.name = 'Gray'; console.log(person1.hasOwnProperty('name')); // true
var person2 = new Person(); console.log(person2.hasOwnProperty('name')); // false
delete person1.name; console.log(person1.hasOwnProperty('name')); // false

Object.keys() 得到对象上全部可枚举的实例属性

// Object.keys()
function Person() { } Person.prototype.name = 'Mike'; Person.prototype.age = 28; Person.prototype.job = 'teacher'; Person.prototype.sayName = function() { console.log(this.name); } console.log(Object.keys(Person.prototype)); // ["name", "age", "job", "sayName"]
var person1 = new Person(); console.log(Object.keys(person1)); // []
person1.name = 'Gray'; console.log(Object.keys(person1)); // ["name"]

更简单的原型语法

function Person() { } Person.prototype = { // prototype 的 constructor 属性再也不指向 Person
    name: 'Mike', age: 28, job: 'Teacher', sayName: function() { console.log(this.name); } } var person1 = new Person(); console.log(person1)

以上方式,Person.prototype 的 constructor 属性将再也不指向 Person。

对比下面两张图:

Person.prototype.name = 'Mike'; // 方式建立的

constructor 会指向 Person

Person.prototype = {}; // 对象字面量方式建立的

constructor 不会指向 Person

经过 instanceof 还能返回正确的结果,可是 constructor 已经不能肯定对象的类型了。

console.log(person1 instanceof Object); // true
console.log(person1 instanceof Person); // true
console.log(person1.constructor == Object); // true
console.log(person1.constructor == Person); // false

若是 constructor 很重要,能够显示指定

// 显示指定 constructor
function Person() { } Person.prototype = { // 经过指定 constructor属性,指向 Person
    constructor: Person, // 以这种方式重设,会使它的[[Enumerable]]特性被设置为true
    name: 'Mike', age: 28, job: 'Teacher', sayName: function() { console.log(this.name); } } var person1 = new Person(); console.log(person1); // 打印出的结果见下图

兼容 ECMAScript5的浏览器引擎

Object.defineProperty()

function Person() { } Person.prototype = { name: 'Mike', age: 28, job: 'Teacher', sayName: function() { console.log(this.name); } } Object.defineProperty(Person.prototype, 'constructor', { enumerable: false, value: Person });

原型的动态性

先建立实例,再在Person的原型对象上加方法,实例也能够调用。

// 原型的动态性

function Person() { } var person1 = new Person(); Person.prototype.sayHi = function() { console.log('Hi'); } person1.sayHi(); // Hi

重写原型对象

function Person() { } var person1 = new Person(); Person.prototype = { // 经过指定 constructor属性,指向 Person
 constructor: Person, name: 'Mike', age: 28, job: 'Teacher', sayName: function() { console.log(this.name); } } console.log(person1); person1.sayName(); // person1.sayName is not a function

能够看到,person1的原型对象上没有Person的新原型对象上的属性,由于他们是两个不一样的对象。

下图展现了重写原型以前和重写原型以后各个对象之间的关系。

 

 

原生对象的原型

全部原生引用类型(Object, Array, String等)都是在其构造函数的原型上定义了方法。

console.log(typeof Array.prototype.sort); // "function"

在原生对象的原型上添加方法

// 注意 es6 中已经实现了该方法
String.prototype.startsWith = function(text) { return this.indexOf(text) === 0; } console.log('Hello javascript'.startsWith('Hello')); // true

不推荐在原生对象的原型上添加方法,可能会意外的重写原生方法。

 

原型对象的问题

缺点:

  • 省略了为构造函数传递初始化参数
  • 全部属性被实例共享——主要针对引用类型的属性

 

看下面代码

// 原型对象缺点
function Person() { } Person.prototype = { constructor: Person, name: 'Mike', age: 28, job: 'Teacher', friends: ['Emily', 'David'], sayName: function() { console.log(this.name); } } var person1 = new Person(); var person2 = new Person(); person1.friends.push('Jennifer'); console.log(person1.friends); // ["Emily", "David", "Jennifer"]
console.log(person2.friends); // ["Emily", "David", "Jennifer"]
console.log(person1.friends == person2.friends); // true

person1 的friends 和 person2 的 friends 共享了,实际应用中,每一个实例应该都有本身的 friends才对。

因此,出现了如下这种模式。

组合模式 (组合使用构造函数模式和原型模式)

优势:

  • 构造函数定义实例属性,每一个实例都会有一份本身的实例属性副本
  • 原型模式定义方法和共享属性,实例可共享方法,节省内存
  • 支持传参

改写前面的例子:

// 组合模式
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.friends = ['Emily', 'David']; } Person.prototype = { constructor: Person, sayName: function() { console.log(this.name); } } var person1 = new Person('Mike', 28, 'teacher'); var person2 = new Person('Gray', 24, 'doctor'); person1.friends.push('Jennifer'); console.log(person1.friends); // ["Emily", "David", "Jennifer"]
console.log(person2.friends); // ["Emily", "David"]
console.log(person1.friends == person2.friends); // false
person1.sayName(); // Mike
person2.sayName(); // Gray

如今修改任一实例的 friends 属性,都只会影响自身,不会影响到其余实例的。

动态原型模式

组合模式中,构造函数和原型是分离的。

动态原型模式,将原型在构造函数初始化时,就在构造函数中初始化了。

function Person(name, age, job) { this.name = name; this.age = age; this.job = job; if (typeof this.sayName != 'function') { Person.prototype.sayName = function() { // 能够当即在全部实例中体现
            console.log(this.name); } } } var person1 = new Person('Mike', 28, 'teacher'); person1.sayName(); // Mike

寄生构造函数模式

先来看一段代码:

function Person(name, age, job) { var obj = new Object(); // <-- 建立一个 obj
    obj.name = name; obj.age = age; obj.job = job; obj.sayName = function() { console.log(this.name); } return obj; // <-- return obj
} var person1 = new Person('Mike', 28, 'teacher'); person1.sayName(); // Mike

看起来和构造函数没什么区别,就是将属性都绑定在内部新建的 obj 上,而后返回这个 obj。

可是,请继续看:

console.log(person1.constructor); // ƒ Object() { [native code] } 实例的构造函数是 Object 不是 Person
console.log(person1 instanceof Person); // false <-- 实例

构造函数中返回的对象与构造函数或构造函数的原型属性之间没有关系。不能依赖 instanceof 来肯定对象的类型。

建议:在可使用其余模式时,不要用这种模式。

稳妥构造函数模式

稳妥对象(durable objects):没有公共属性;方法不引用 this。

稳妥对象适合在一些安全环境(禁止使用 this 和 new)和防止数据被其余应用程序改动时使用。

function Person(name, age, job) { var obj = new Object(); obj.name = name; obj.age = age; obj.job = job; obj.sayName = function() { console.log(name); // <-- 方法不引用 this
 } return obj; } var person1 = Person('Mike', 28, 'teacher'); // 不使用 new 关键字
person1.sayName(); // Mike

其中 name 的值,只能经过 sayName 方法访问。

 

注:以上全部的文字、代码都是本人一个字一个字敲上去的,图片也是一张一张画出来的,转载请注明出处,谢谢!

相关文章
相关标签/搜索