您现在的位置是:网站首页> 内容页

创建对象学习 —— 《高级教程》

  • 利来国际网址
  • 2019-04-01
  • 348人已阅读
简介创建单个对象的缺点:用同一个接口创建很多对象,会产生大量的重复代码。工厂模式就是为了解决这个问题。工厂模式解决了创建多个相似对象的问题functioncreatePerson(nam

创建单个对象的缺点:用同一个接口创建很多对象,会产生大量的重复代码。

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

工厂模式

解决了创建多个相似对象的问题

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语句

构造函数本身也是函数,只不过可以用来创建对象

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

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

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

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

person1 和 person2 都是 Person 的实例,也是 Object 的实例,可以通过 instanceof 操作符来检验。

console.log(person1 instanceof Person); // trueconsole.log(person2 instanceof Person); // trueconsole.log(person1 instanceof Object); // trueconsole.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)); // trueconsole.log(Person.prototype.isPrototypeOf(person2)); // true

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

console.log(Object.getPrototypeOf(person1) == Person.prototype); // trueconsole.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(); // Grayperson2.sayName(); // Mikeconsole.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(); // Grayperson2.sayName(); // Mikeperson1.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(); // Grayperson2.sayName(); // Mikedelete 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")); // falseperson1.name = "Gray";console.log(person1.hasOwnProperty("name")); // truevar person2 = new Person();console.log(person2.hasOwnProperty("name")); // falsedelete 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); // trueconsole.log(person1 instanceof Person); // trueconsole.log(person1.constructor == Object); // trueconsole.log(person1.constructor == Person); // false

如果 constructor 很重要,可以显示指定

// 显示指定 constructorfunction 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的新原型对象上的属性,因为他们是两个不同的对象。

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

 

 未完待续...

 

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

文章评论

Top