《每天十分钟》-红宝书第4版-对象、类与面向对象编程(二)

和面向对象相关的内容开始了,认真抄,用了很多年 ,要多思考。
ECMAScript 5.1 并没有正式支持面向对象的结构,比如类或继承。ECMAScript 6 开始正式支持类和继承。ES6 的类旨在完全涵盖之前规范设计的基于原型的继承模式。 ES6 的类都仅仅是封装了 ES5.1 构造函数加原型继承的语法糖而已。

工厂模式

工厂模式是一种众所周知的设计模式,广泛应用于软件工程领域,用于抽象创建特定对象的过程。比如这个简单的方法解决了创建多个类似对象的问题,但是没有解决对象标识问题(新创建的对象是什么类型)

function createPerson(name, age, job) { 
 let o = new Object(); 
 o.name = name; 
 o.age = age; 
 o.job = job; 
 o.sayName = function() { 
 console.log(this.name); 
 }; 
 return o; 
} 
let person1 = createPerson("Nicholas", 29, "Software Engineer"); 
let person2 = createPerson("Greg", 27, "Doctor");

构造函数模式

ECMAScript 中的构造函数是用于创建特定类型对象的。比如:

function Person(name, age, job){ 
 this.name = name; 
 this.age = age; 
 this.job = job; 
 this.sayName = function() { 
 console.log(this.name); 
 }; 
} 
let person1 = new Person("Nicholas", 29, "Software Engineer"); 
let person2 = new Person("Greg", 27, "Doctor"); 
person1.sayName(); // Nicholas 
person2.sayName(); // Greg

构造函数名称的首字母都是要大写的,非构造函数则以小写字母开头。这是从面向对象编程语言那里借鉴的,有助于在 ECMAScript 中区分构造函数和普通函数。毕竟 ECMAScript 的构造函数就是能创建对象的函数。
要创建 Person 的实例,应使用 new 操作符。以这种方式调用构造函数会执行如下操作。(面试常问。。)
(1) 在内存中创建一个新对象。
(2) 这个新对象内部的[[Prototype]]特性被赋值为构造函数的 prototype 属性
(3) 构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)。
(4) 执行构造函数内部的代码(给新对象添加属性)。
(5) 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
上一个例子的最后,person1 和 person2 分别保存着 Person 的不同实例。这两个对象都有一个constructor 属性指向 Person

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

constructor 本来是用于标识对象类型的。不过,一般认为 instanceof 操作符是确定对象类型更可靠的方式。

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

定义自定义构造函数可以确保实例被标识为特定类型,相比于工厂模式,这是一个很大的好处。

在实例化时,如果不想传参数,那么构造函数后面的括号可加可不加。只要有 new 操作符,就可以调用相应的构造函数。

let person1 = new Person(); 
let person2 = new Person;

构造函数也是函数

// 作为构造函数 
let person = new Person("Nicholas", 29, "Software Engineer"); 
person.sayName(); // "Nicholas" 
// 作为函数调用
Person("Greg", 27, "Doctor"); // 添加到 window 对象
window.sayName(); // "Greg" 
// 在另一个对象的作用域中调用
let o = new Object(); 
Person.call(o, "Kristen", 25, "Nurse"); 
o.sayName(); // "Kristen"

构造函数的问题

构造函数的主要问题在于,其定义的方法会在每个实例上都创建一遍。

function Person(name, age, job){ 
 this.name = name; 
 this.age = age; 
 this.job = job; 
 this.sayName = sayName; 
} 
function sayName() { 
 console.log(this.name); 
} 
let person1 = new Person("Nicholas", 29, "Software Engineer"); 
let person2 = new Person("Greg", 27, "Doctor"); 
person1.sayName(); // Nicholas 
person2.sayName(); // Greg

在这里,sayName()被定义在了构造函数外部。在构造函数内部,sayName 属性等于全局 sayName()函数。因为这一次 sayName 属性中包含的只是一个指向外部函数的指针,所以 person1 和 person2共享了定义在全局作用域上的 sayName()函数。这样虽然解决了相同逻辑的函数重复定义的问题,但全局作用域也因此被搞乱了,因为那个函数实际上只能在一个对象上调用。如果这个对象需要多个方法,那么就要在全局作用域中定义多个函数。这会导致自定义类型引用的代码不能很好地聚集一起。这个新问题可以通过原型模式来解决。

下次再抄这个非常非常重要的原型模式

送陈七赴西军 唐 孟浩然
吾观非常者,碌碌在目前。
君负鸿鹄志,可惜书剑年。
一闻边烽动,万里忽争先。
余亦赴京国,何当献凯还。

你可能感兴趣的:(《每天十分钟》红宝书第4版,javascript)