目录
1.创建对象
ECMAScript中没有类的概念,可以把 ECMAScript的对象想象成一张散列表,其中的内容就是一组名/值对 属性的类型分两种:数据属性和访问器属性
(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");
(2)构造函数模式
person1 和 person2 分别保存着 Person 的不同实例。这两个对象都有一个constructor 属性指向 Person?
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
constructor属性和instanceof 操作符都可以用来确定对象类型?
console.log(person1.constructor == Person); // true
console.log(person1 instanceof Person); // true
优点:相比于工厂模式,自定义构造函数可以确保实例被标识为特定类型 缺点:?其定义的方法会在每个实例上都会重新创建一遍
(3)原型模式?
每个函数都会创建一个 prototype 属性,这个属性指向一个原型对象。该对象可以让所有对象实例共享一些属性和方法。? 原型对象有一个名为 constructor 的属性,指回与之关联的构造函数。 每次调用构造函数创建一个新实例,该实例内部的[[Prototype]]指针就会指向构造函数的原型对象。 也就是说:实例与构造函数原型之间有直接的联系,但与构造函数之间没有。
function Person() {} //使用函数表达式也可以let Person = function() {};
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
console.log(this.name);
};
let person1 = new Person();
person1.sayName(); // "Nicholas"
let person2 = new Person();
person2.sayName(); // "Nicholas"
console.log(person1.sayName == person2.sayName); // true
isPrototypeOf()方法可以确定对象实例和构造函数之间的关系
console.log(Person.prototype.isPrototypeOf(person1)); // true
Object.getPrototypeOf() 方法,返回对象实例的内部特性[[Prototype]]的值,也就是原型对象。
console.log(Object.getPrototypeOf(person1) == Person.prototype); // true
通过对象访问属性时,会先搜索对象实例本身,如果该实例上有指定属性,则返回对应的值。如果没有,就会沿着指针进入原型对象,到原型对象上继续搜索。?
hasOwnProperty() 方法用于确定某个属性是在实例上还是在原型对象上,在实例上返回true,在原型对象上返回false。
//还是上面那个构造函数
let person1 = new Person();
let person2 = new Person();
person1.name = "Greg";
console.log(person1.hasOwnProperty("name")); // true
console.log(person2.name); // "Nicholas",来自原型
console.log(person2.hasOwnProperty("name")); // false
delete person1.name; //delete可以删除实例上的属性
console.log(person1.hasOwnProperty("name")); // false
原型和 in 操作符?
in 操作符会在可以通过对象访问指定属性时返回 true ,无论该属性是在实例上还是在原型上。
// 确定某个属性是否存在于原型上
function hasPrototypeProperty(object, name){
return !object.hasOwnProperty(name) && (name in object);
}
for-in循环中使用in操作符,可通过对象访问且可被枚举的属性都会返回,包括实例属性和原型属性。? 获得对象上所有可枚举的实例属性,可以使用 Object.keys() 想列出所有实例属性,无论是否可以枚举,可使用Object.getOwnPropertyNames()
更简单的写法
function Person() {
}
Person.prototype = {
//用字面量的形式创建创建一个原型对象,此时constructor属性指向Object对象
//所以这里要指回去
constructor: Person,
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName() {
console.log(this.name);
}
};
缺点:原型上的所有属性是在实例间共享的,但一般来说,不同的实例应该有属于自己的属性副本
(4)组合使用构造函数模式和原型模式
构造函数用于定义实例属性 原型对象用于存放共享的方法和属性
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor: Person,
sayName: function() {
console.log(this.name);
}
}
let person1 = new Person("Nicholas", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court"
2.继承
①原型链?
由于接口继承只继承方法签名,而由于JS中的函数没有签名,所以JS无法实现接口继承 可以依靠原型链来实现继承 基本思想:?利用原型让一个引用类型继承另一个引用类型的属性和方法 所有引用类型都继承自 Object ,这也是通过原型链实现的
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
};
function SubType() {
this.subproperty = false;
}
// 继承 SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
return this.subproperty;
};
let instance = new SubType();
console.log(instance.getSuperValue()); // true
?确定原型与继承关系
// instanceof 操作符
console.log(instance instanceof Object); // true
console.log(instance instanceof SuperType); // true
console.log(instance instanceof SubType); // true
// isPrototypeOf() 方法
console.log(Object.prototype.isPrototypeOf(instance)); // true
console.log(SuperType.prototype.isPrototypeOf(instance)); // true
console.log(SubType.prototype.isPrototypeOf(instance)); // true
以对象字面量方式创建原型方法会破坏之前的原型链
// 继承 SuperType
SubType.prototype = new SuperType();
// 通过对象字面量添加新方法,这会导致上一行无效
SubType.prototype = {
getSubValue() {
return this.subproperty;
},
someOtherMethod() {
return false;
}
};
let instance = new SubType();
console.log(instance.getSuperValue()); // 出错!
问题
①对象实例共享所有继承的属性和方法 ②在创建子类型的实例时,不能向超类型的构造函数中传递参数?
②借用构造函数
基本思路:在子类构造函数中调用父类构造函数。 相比于使用原型链的优点:可以在子类构造函数中向父类构造函数传参。?
function SuperType(name){
this.name = name;
}
function SubType() {
SuperType.call(this, "Nicholas"); // 继承 SuperType 并传参
this.age = 29; // 实例属性
}
let instance = new SubType();
console.log(instance.name); // "Nicholas";
console.log(instance.age); // 29
问题:必须在构造函数中定义方法,因此函数不能重用。
③组合继承
综合了原型链和盗用构造函数
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age){
// 继承属性
SuperType.call(this, name); // 第二次调用SuperType构造函数
this.age = age;
}
// 继承方法
SubType.prototype = new SuperType(); // 第一次调用SuperType构造函数
SubType.prototype.sayAge = function() {
console.log(this.age);
};
let instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
instance1.sayName(); // "Nicholas";
instance1.sayAge(); // 29
let instance2 = new SubType("Greg", 27);
console.log(instance2.colors); // "red,blue,green"
instance2.sayName(); // "Greg";
instance2.sayAge(); // 27
?问题:会调用两次超类型的构造函数,见上
④寄生式组合继承
function inheritPrototype(subType, superType) {
let prototype = object(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 赋值对象
}
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
console.log(this.age);
};
|