ECMA-262将对象定义为一组属性的无序集合。
一、对象创建的方式
- 通过构造函数的方式new Object()
var person = new Object()
person.name = '张三';
person.age = 30;
person.sayName = function () {
console.log(this.name);
}
复制代码
- 通过字面量的方式{}
var person = {
name: '张三',
age: 30,
sayName: function () {
console.log(this.name);
}
}
复制代码
二、继承的方式
1、原型链
// 父级构造函数
function SuperFun() {
this.name = 'qb';
this.colors = ['red', 'green'];
}
SuperFun.prototype.getName = function () {
return this.name;
}
// 子构造函数
function SubFun() {
this.age = 30;
};
// 原型继承
SubFun.prototype = new SuperFun();
SubFun.prototype.getAge = function () {
return this.age;
}
let obj = new SubFun();
console.log(obj.getName()); // 'qb'
console.log(obj.getAge()); // '30'
复制代码
以上通过SubFun.prototype = new SuperFun()的方式,将SubFun的原型以实例化SuperFun产生的对象进行赋值。实例化的对象上有name属性和getName的方法,因此,SubFun实例化的对象obj也可以进行name属性的获取和getName方法的调用。
存在的问题
- 1、SubFun的构造函数指向了SuperFun,而不是预期的SubFun
console.log(obj.constructor === SubFun) // false
console.log(obj.constructor === SuperFun) // true
复制代码
- 2、SubFun实例化的对象会共享原型上的引用数据,非各自独有
obj.colors.push('pink');
console.log(obj.colors); // ["red", "green", "pink"]
let obj2 = new SubFun();
console.log(obj2.colors); //["red", "green", "pink"]
复制代码
2、盗用构造函数
// 父级构造函数
function SuperFun() {
this.name = 'qb';
this.colors = ['red', 'green'];
}
SuperFun.prototype.getName = function () {
return this.name;
}
// 子构造函数,并且盗用构造函数
function SubFun() {
SuperFun.call(this);
};
let obj = new SubFun();
obj.colors.push('pink');
console.log(obj.colors); // ["red", "green", "pink"]
let obj2 = new SubFun();
console.log(obj2.colors); //["red", "green"]
复制代码
以上通过在SubFun中去使用call方法,把SuperFun中的属性赋值给SubFun,而且,每次实例化SubFun的时候都去执行SuperFun.call(this),这样,每次产生的都是新的引用数据。
存在的问题
- SubFun实例化对象中无法访问SuperFun原型上的方法
console.log(obj.getName());
复制代码
以上代码的执行结果是:
3、组合继承
// 父级构造函数
function SuperFun(name) {
this.name = name;
this.colors = ['red', 'green'];
}
SuperFun.prototype.getName = function () {
return this.name;
}
function SubFun(name, age) {
// (1)继承:继承属性
SuperFun.call(this, name);
this.age = age;
};
// (2)继承:继承方法
SubFun.prototype = new SuperFun();
SubFun.prototype.getAge = function () {
return this.age;
}
let obj = new SubFun('qb', '30');
console.log(obj.getName()); // qb
console.log(obj.getAge(30)); // 30
obj.colors.push('pink');
console.log(obj.colors); // ["red", "green", "pink"]
let obj2 = new SubFun('qb2', '31');
console.log(obj2.colors); //["red", "green"]
复制代码
以上结合原型继承方法和盗用构造函数继承属性的方式进行结合,解决了SubFun实例化的对象会共享原型上的引用数据的问题,既可以把定义在父级原型上的方法进行重用,又可以让每个实例都有自己的属性。
存在的问题
- SubFun的构造函数指向了SuperFun,而不是预期的SubFun的问题依然存在
console.log(obj.constructor === SubFun) // false
console.log(obj.constructor === SuperFun) // true
复制代码
4、原型式继承
- 实现1
// 原型式继承
function object(o) {
function F() {};
F.prototype = o;
return new F();
}
var obj = {
name: 'qb',
colors: ['red', 'gree']
}
var obj1 = object(obj);
obj1.colors.push('pink');
console.log(obj1.colors); // ["red", "gree", "pink"]
var obj2 = object(obj);
console.log(obj2.colors); // ["red", "gree", "pink"]
复制代码
以上通过object函数,把目标对象o传入,作为空白构造函数的原型,然后通过实例化空白构造函数的方式返回新的对戏,新返回的对象就是以目标对象为原型新对象。但是,因为原型的对象总o,所以新创建的各个实例之间共享原型上的引用数据。
- 实现2
var obj = {
name: 'qb',
colors: ['red', 'gree']
}
var obj1 = Object.create(obj);
obj1.colors.push('pink');
console.log(obj1.colors); // ["red", "gree", "pink"]
var obj2 = Object.create(obj);
console.log(obj2.colors); // ["red", "gree", "pink"]
复制代码
以上Object.create的实现和object函数实现效果一样。
存在的问题
- 实例化的对象会共享原型上的引用数据,非各自独有
5、寄生式继承
function createObj(o) {
let clone = Object.create(o); // 通过调用Object.create创建一个以o为原型的新对象
clone.sayHi = function () { // 通过定义方法增强函数
console.log('hi');
}
return clone;
}
var obj = {
name: 'qb',
colors: ['red', 'gree']
}
var obj1 = createObj(obj);
obj1.colors.push('pink'); // ["red", "gree", "pink"]
obj1.sayHi(); // hi
console.log(obj1.colors);
var obj2 = createObj(obj);
obj2.sayHi(); // hi
console.log(obj2.colors); // ["red", "gree", "pink"]
复制代码
存在的问题
- 和原型式继承一样,实例化的对象会共享原型上的引用数据而非各自独有
6.寄生式组合继承
// 父级构造函数
function SuperFun(name) {
this.name = name;
this.colors = ['red', 'green'];
}
SuperFun.prototype.getName = function () {
return this.name;
}
function SubFun(name, age) {
SuperFun.call(this, name); // (1)继承:继承属性
this.age = age;
};
SubFun.prototype = Object.create(SuperFun.prototype); // (2)继承:继承原型上的方法
SubFun.prototype.constructor = SubFun; // 注意:SubFun原型上的constructor重新指向它自己
SubFun.prototype.sayAge = function () {
return this.age;
}
var obj = new SubFun('qb', 30);
console.log(obj); // {name: 'qb', colors: ['red', 'green'], age: 30}
console.log(obj.sayAge());
console.log(obj.getName());
obj.colors.push('pink');
console.log(obj); // {name: 'qb', colors: ['red', 'green', 'pink'], age: 30}
var obj2 = new SubFun('qb', 30);
console.log(obj2); // {name: 'qb', colors: ['red', 'green'], age: 30}
复制代码
以上通过SuperFun.call(this, name)的方式,让子类构造函数初始化父构造中的代码,结果就是让每个实例都拥有自己的colors属性,并通过Object.create继承父级构造函数的原型,继承其上的属性和方法。此时,SubFun的构造函数还是SuperFun,再通过SubFun.prototype.constructor = SubFun的方式指回SubFun。
对象是一组属性的无序集合,继承可以让对象的产生有迹可循,有类可分。