构造函数会在每一个实例上都创建一遍!
使用原型模式定义的属性和方法由所有实例共享!
原型链

1
2
3
4
5
6
7
8
9
10
function Parent(){
this.name = 'kevin';
}
Parent.prototype.getName=function() {
console.log(this.name);
}
function Child() {}
Child.prototype = new Parent(); //表明继承关系,还没有创建child1对象
var child1 = new Child();// 创建child1对象
console.log(child1.getName())//kevin

缺点
1.引用类型的属性被所有实例共享
2.创建Child实例时,不能向Parent传参
借用构造函数(经典继承)

1
2
3
4
5
6
7
8
9
10
11
function Parent(){
this.names=['kevin','daisy'];
}
function Child(){
Parent.call(this);
}
var child1 = new Child();
child1.names.push('yayu');
console.log(child1.names);//["kevin","daisy","yayu"]
var child2 = new Child();
console.log(child2.names);//["kevin","daisy"]

1.避免了引用类型的属性被所有实例共享
2.可以在Child中向Parent传参
缺点
方法都在构造函数中定义,每次创建实例都会创建一遍方法
组合继承
原型链继承+经典继承双创合璧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Parent(name){
this.name=name;
this.colors=['red','blue','green'];
Parent.prototype.getName = function () {
console.log(this.name)
}
}
function Child(name,age){
Parent.call(this,name);
this.age=age;
}
Child.prototype = new Parent();//表明继承关系
Child.prototype.constructor = Child;//修正了构造函数的指向。
var child1 = new Child('kevin','18');
child1.colors.push('black');
console.log(child1.name);//kevin
console.log(child1.age);//18
console.log(child1.colors);//["red","blue","green","black"]
var child2 = new Child('daisy','20');
console.log(child2.name);//daisy
console.log(child2.age);//20
console.log(child2.colors);//["red","blue","green"]

融合原型链继承和构造函数的优点,JS最常用继承模式
原型式继承

1
2
3
4
5
function createobj(o){
function F(){}
F.prototype = o;
return new F();
}

一个使用这种方法实现继承的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 定义一个父类对象
let parentObj = {
name: 'Parent',
sayHello: function () {
console.log(`Hello from ${this.name}`);
}
};

// 使用 createobj 函数实现继承
function createobj(o) {
function F() {}
F.prototype = o;
return new F();
}

// 创建一个继承自 parentObj 的子类对象
let childObj = createobj(parentObj);
childObj.name = 'Child';

// 调用继承自父类的方法
childObj.sayHello();

ES5 Object.create的模拟实现,将传入对象作为创建的对象的原型
包含引用类型的属性值都会共享相应的值一—和原型链继承一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function createobj(o) {
function F() {}
F.prototype = o;
return new F();
}
var person = {
name:'kevin',
friends:['daisy','kelly']
}
var person1 = createobj(person);
var person2 = createobj(person);
person1.name = 'person1';
console.log(person2.name);//kevin
person1.friends.push('taylor');
console.log(person2.friends);//["daisy","kelly","taylor"]

寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式做增强对象,再返回对象

1
2
3
4
5
6
function createobj(o){
var clone = Object.create(o);
clone.sayName = function ()
console.log('hi');
return clone;
}

缺点
和借用构造函数一样,每次创建对象都会创建一遍方法
寄生组合式继承
组合继承最大缺点一调用2次父构造方法
1.设置子实例的原型时
2.创建子类型的实例时
如何避免重复调用?
如果我f们不使用Child.prototy pe=new Parent(),而是间接让Child.prototype访问Parent..prototypel呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function Parent(name){
this.name=name;
this.colors=['red','blue','green'];
Parent.prototype.getName function ()
console.log(this.name)
}
function Child(name,age){
Parent.call(this,name);
this.age age;
//关键的三步
var F = function ({}
F.prototype = Parent.prototype;
Child.prototype = new F();
var child1 = new Child('kevin','18');
console.log(child1);
封装一下这个继承方法
function object(o)
function F(){}
F.prototype o;
return new F();
function prototype(child,parent){
var prototype = object(parent.prototype);//创建父类原型对象的副本
prototype.constructor = child;//增强对象,补充因重写原型而失去的默认的constructor/属性
child.prototype = prototype;//将创建的新副本指定给子类的原型对象
}
//当我们使用的时候:
prototype(Child,Parent);

优点一引用类型最理想的继承方式
只调用一次Parent构造函数,避免了在Parent.prototype.上面创建不必要的、多余的属性
同时,原型链能保持不变,能正常使用instanceof和isPrototypeOf

————————————-分割线———————————–

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// 定义构造函数*(普通函数加个this)
function Animal(name) {
this.name = name;
}

// 定义构造函数的原型方法, 在构造函数的prototype上注册了speak方法
Animal.prototype.speak = function() {
console.log(this.name + ' makes a noise.');
};

// 定义另一个构造函数
function Dog(name) {
Animal.call(this, name); // 继承 Animal 的属性
}

// Dog 继承 Animal 的方法,先声明prototype, 再指定prototype.constructor
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.speak = function() {
console.log(this.name + ' barks.');
};

// 创建实例
const myDog = new Dog('Rex');

// 使用 instanceof 检查
console.log(myDog instanceof Dog); // true
console.log(myDog instanceof Animal); // true
console.log(myDog instanceof Object); // true

// 逐步调试:自定义 instanceof 实现, 用Object.getPrototypeOf循环遍历left的prototype, 查看是否和right的prototype相等
function myInstanceof(left, right) {
console.log(`Checking if ${left} is an instance of ${right.name}`);

// 如果左边是 null 或 undefined,直接返回 false
if (left === null || typeof right !== 'function') {
return false;
}

// 获取左边对象的原型
let proto = Object.getPrototypeOf(left);
const prototype = right.prototype;

// 沿着原型链查找
while (proto !== null) {
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}

return false;
}

// 调试 myInstanceof 函数
console.log(myInstanceof(myDog, Dog)); // true
console.log(myInstanceof(myDog, Animal)); // true
console.log(myInstanceof(myDog, Object)); // true
console.log(myInstanceof(myDog, Array)); // false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 定义一个构造函数 Person
function Person(name, age) {
this.name = name;
this.age = age;
}

// 向构造函数的 prototype 添加一个方法
Person.prototype.sayHello = function() {
console.log('Hello, my name is ' + this.name);
};

// 创建一个实例对象
var person1 = new Person('Alice', 25);
var person2 = new Person('Bob', 30);

// 查看实例对象的属性和原型链
console.log('person1 的 name:', person1.name); // 输出: Alice
console.log('person1 的 age:', person1.age); // 输出: 25

// 调用 prototype 上的方法
person1.sayHello(); // 输出: Hello, my name is Alice
person2.sayHello(); // 输出: Hello, my name is Bob

// 查看实例的 __proto__,它指向构造函数的 prototype, new 的对象具有__proto__属性,instance.__proto__和class的prototype相等
console.log('person1.__proto__ === Person.prototype:', person1.__proto__ === Person.prototype); // true

// 查看构造函数的 prototype
console.log('Person.prototype:', Person.prototype); // 输出: { sayHello: f }

// 查看实例 person1 的 __proto__
console.log('person1.__proto__:', person1.__proto__); // 输出: { sayHello: f }

// 验证 person1 是否是 Person 的实例
console.log('person1 instanceof Person:', person1 instanceof Person); // 输出: true

// 查看 Object.prototype,所有对象最终的原型
console.log('Person.prototype.__proto__ === Object.prototype:', Person.prototype.__proto__ === Object.prototype); // true

// 创建一个普通的对象,不通过构造函数
var obj = { greeting: 'Hi' };
console.log('obj.__proto__ === Object.prototype:', obj.__proto__ === Object.prototype); // true
console.log('obj 是 Object 的实例:', obj instanceof Object); // true