1. 对象的创建
// 创建person对象
let person = new Object();
person.name = "张三";
person.age = 18;
person.sayName = function(){
console.log(this.name);
}
// 对象字面量
let person = {
name:"张三",
age:18,
sayName(){
console.log(this.name);
}
};
// 可以使用Object.defineProperty()来修改对象默认属性
Object.defineProperty(person, "name", {
Writable: false, // 数据属性,表示属性的值不可被修改
value: "mike"
})
console.log(person.name) //mike
person.name = "jack";
console.log(person.name) //mike
//访问器属性 getter\setter
let book = {
year_:2017, //_常用来表示私有属性
edition:1
};
Object.defineProperty(book, "year", {
get(){
return this.year_;
},
set(newValue){
if(newValue > 2017) {
this.year_ = newValue;
this.edition += newValue - 2017;
}
}
});
book.year = 2018;
console.log(book.edition); //2
//同时定义多个属性
let book = {};
Object.defineProperties(book, {
year_:{
value: 2017
},
edition: {
value: 1
},
year: {
get(){
return this.year_;
},
set(newValue){
if(newValue > 2017) {
this.year_ = newValue;
this.edition += newValue - 2017;
}
}
}
});
1.1 工厂模式
function createPerson(name, age, job){
let o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName(){
console.log(this.name);
};
return o;
}
let person = createPerson("Greg", 23, "Student");
1.2 构造函数模式
任何函数只要使用了new操作符调用就是构造函数。
//区别
// 1.没有显示的创建对象
// 2.属性和方法直接赋值给this
// 3.没有return
funciton Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName(){
console.log(this.name);
};
}
let person1 = new Person("Tom", 18, "Student");
let person2 = new Person("Jack", 18, "Student");
console.log(person1.constructor == Person); // true
console.log(person1.sayName === person2.sayName); // false 不同实例上的函数虽然同名但不相等
面试题:new操作符做了些什么
- 在内存中创建了一个对象;
- 在这个新对象的内部的Prototype特性被赋值为构造函数的prototype属性;
- 构造函数内部的this指向新对象;
- 执行构造函数内部的代码(给新对象添加属性);
- 如果构造函数返回非空对象,则返回该对象;否则返回刚创建的新对象。
1.3 原型模式
原型
函数即为对象,对象则有属性,在JS中将函数的prototype属性称为原型,也称显式原型,prototype指向的对象就是原型对象,所有原型对象自动获取constructor属性,指回与之关联的构造函数。每个对象会有隐式原型__proto__属性,通过这个属性可以访问对象的原型。
每个函数都会创建一个prototype属性,这个属性是一个对象,包含应该由特点引用类型的实例共享的属性和方法。
function Person() {};
Person.prototype.name = "Jerry";
Person.prototype.age = 29;
Person.prototype.job = "SE";
Person.prototype.sayName = function() {
console.log(this.name);
};
let person1 = new Person();
person1.sayName(); // "Jerry"
let person2 = new Person();
person2.sayName(); // "Jerry"
console.log(person1.sayName == person2.sayName); // true
console.log(Person.prototype.constructor === Person); // true
// 构造函数Person、原型对象Person.prototype、实例person1是三种完全不同的对象
console.log(person1.__proto__ === Person.prototype); // true
console.log(person1.__proto__ === person2.__proto__); // true
console.log(Person.prototype.isPrototypeOf(person1)); // true
console.log(Object.getPrototypeOf(person1) == Person.prototype); // true
2. 继承
1. 原型链
原型本身有一个内部指针指向另一个原型,相应的另一个原型也有一个指针指向另一个构造函数,这样实例与原型之间构造了一条原型链。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subproperty = false;
}
// 继承
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
return this.subproperty;
}
let instance = new SubType();
console.log(instance.getSuperValue()); // true
问题:
- 原型中包含的引用值会在所有实例间共享,容易造成修改混乱;
- 子类型在实例化时不能给父类型的构造函数传参。
解决方法:
- 盗用构造函数:通过call()或者apply()方法在子类构造函数中调用父类构造函数
缺点:必须在构造函数中定义方法,因此函数不能重用;子类也不能够访问父类原型上定义的方法。
function SuperType(){
this.colors = ["red","green","blue"];
}
function SubType(){
SuperType.call(this);
}
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); //"red,green,blue,black"
let instance2 = new SubType();
console.log(instance2.colors); //"red,green,blue"
2. 组合继承
使用原型链继承原型上的属性和方法,通过盗用构造函数继承实例属性。
缺点:父类构造函数始终会被调用两次
function SuperType(name){
this.name = name;
this.colors = ["red","green","blue"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age){
// 继承属性
SuperType.call(this, name);
this.age = age;
}
// 继承方法
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function() {
console.log(this.age);
}
let instance1 = new SubType("Tom",29);
instance1.colors.push("black");
console.log(instance1.colors); //"red,green,blue,black"
instance1.sayName(); //"Tom"
instance1.sayAge(); //29
let instance2 = new SubType("Greg",27);
console.log(instance2.colors); //"red,green,blue"
instance2.sayName(); //"Greg"
instance2.sayAge(); //27
3. 原型式继承
不自定义类型也可以通过原型实现对象之间的信息共享。
let person = {
name: "Tom",
friends: ["Shelby", "Van"]
}
let person1 = Object.create(person);
person1.name = "Greg";
person1.friends.push("Bob");
let person2 = Object.create(person);
person2.name = "Linda";
person2.friends.push("Josh");
console.log(person.friends); // "Shelby,Van,Bob,Josh"
4. 寄生式组合继承
通过盗用构造函数继承属性,使用混合式原型链集成方法,不通过调用父类构造函数给子类原型赋值,是引用类型继承的最佳模式。
function inheritPrototype(subType, superType){
let prototype = object(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 赋值对象
}