# 原型与原型链
# for in
能否把原型链循环出来
能。
# 如何避免?
使用 hasOwnProperty
。
const object1 = new Object();
object1.property1 = 42;
console.log(object1.hasOwnProperty('property1'));
// expected output: true
1
2
3
4
5
2
3
4
5
# 原型链
function Father () {
this.property = true;
}
Father.prototype.getFatherValue = function() {
return this.property
}
function Son() {
this.sonProperty = false;
}
// 继承Father
Son.prototype = new Father(); // Son.prototype被重写,导致Son.prototype.constructor也被重写
Son.prototype.getSonValue = function () {
return this.sonProperty;
}
var instance = new Son();
alert(instance.getFatherValue())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
instance实例通过原型链找到了Father原型中的getFatherValue方法.
# 如何判断原型和实例的继承关系
instance instanceof Object;
Object.prototype.isPrototypeOf(instance);
Object.getPrototypeOf(obj)
1
2
3
4
5
2
3
4
5
问题一:当原型链中包含引用类型值的原型时,该引用类型值会被所有实例共享;
问题二:在创建子类型(例如创建Son的实例)时,不能向超类型(例如Father)的构造函数中传递参数.
# 继承
通过【某种方式】让一个对象可以访问到另一个对象中的属性和方法,我们把这种方式称之为继承
# 为什么要使用继承
有些对象会有方法(动作、行为),而这些方法都是函数,如果把这些方法和函数都放在构造函数中声明就会导致内存的浪费
function Person(){
this.say=function(){
console.log("你好")
}
}
var p1=new Person();
var p2=new Person();
console.log(p1.say === p2.say); //false
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 原型链继承
function Animal(species){
this.species = species
}
Animal.prototype.func = function(){
console.log('Animal')
}
function Cat(){}
/**
* func方法是无效的, 因为后面原型链被重新指向了Animal实例
*/
Cat.prototype.func = function() {
console.log('Cat')
}
Cat.prototype = new Animal()
Cat.prototype.constructor = Cat // 修复: 将Cat.prototype.constructor重新指向本身
/**
* 测试代码
*/
var cat = new Cat()
cat.func() // output: Animal
console.log(cat.species) // undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 经典继承【借用构造函数】
使用call或apply方法,将父对象的构造函数绑定在子对象上
function Father(){
this.colors = ["red","blue","green"];
}
function Son(){
Father.call(this);//继承了Father,且向父类型传递参数
}
var instance1 = new Son();
instance1.colors.push("black");
console.log(instance1.colors);//"red,blue,green,black"
var instance2 = new Son();
console.log(instance2.colors);//"red,blue,green" 可见引用类型值是独立的
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
方法都在构造函数中定义, 因此函数复用也就不可用了.而且超类型(如Father)中定义的方法,对子类型而言也是不可见的. 考虑此,借用构造函数的技术也很少单独使用.
function Animal(name){
this.name = name;
this.sayName = function(){
console.log(this.name)
}
}
Animal.prototype.setName = function(){
console.log('123')
}
function Cat(){
Animal.call(this, 'dog');
}
var cat = new Cat();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 组合继承【伪经典继承】
组合继承, 有时候也叫做伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥两者之长的一种继承模式.
function Father(name){
this.name = name;
this.colors = ["red","blue","green"];
}
Father.prototype.sayName = function(){
alert(this.name);
};
function Son(name,age){
Father.call(this,name);//继承实例属性,第一次调用Father()
this.age = age;
}
Son.prototype = new Father();//继承父类方法,第二次调用Father()
Son.prototype.sayAge = function(){
alert(this.age);
}
var instance1 = new Son("louis",5);
instance1.colors.push("black");
console.log(instance1.colors);//"red,blue,green,black"
instance1.sayName();//louis
instance1.sayAge();//5
var instance1 = new Son("zhai",10);
console.log(instance1.colors);//"red,blue,green"
instance1.sayName();//zhai
instance1.sayAge();//10
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 原型继承
将子对象的prototype指向父对象的一个实例
var o1={ say:function(){} }
var o2=Object.create(o1);
1
2
2
# 寄生式继承
寄生式继承是与原型式继承紧密相关的一种思路
function createAnother(original){
var clone = Object.create(original);//通过调用object函数创建一个新对象
clone.sayHi = function(){//以某种方式来增强这个对象
alert("hi");
};
return clone;//返回这个对象
}
1
2
3
4
5
6
7
2
3
4
5
6
7
使用寄生式继承来为对象添加函数, 会由于不能做到函数复用而降低效率;这一点与构造函数模式类似.
# 寄生组合式继承
寄生组合式继承就是为了降低调用父类构造函数的开销而出现的
function extend(subClass, superClass) {
subClass.prototype = superClass.prototype;
subClass.superclass = superClass.prototype;
if(superClass.prototype.constructor == Object.prototype.constructor) {
superClass.prototype.constructor = superClass;
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
function Father(name){
this.name = name;
this.colors = ["red","blue","green"];
}
Father.prototype.sayName = function(){
alert(this.name);
};
function Son(name,age){
Father.call(this,name);//继承实例属性,第一次调用Father()
this.age = age;
}
extend(Son,Father)//继承父类方法,此处并不会第二次调用Father()
Son.prototype.sayAge = function(){
alert(this.age);
}
var instance1 = new Son("louis",5);
instance1.colors.push("black");
console.log(instance1.colors);//"red,blue,green,black"
instance1.sayName();//louis
instance1.sayAge();//5
var instance1 = new Son("zhai",10);
console.log(instance1.colors);//"red,blue,green"
instance1.sayName();//zhai
instance1.sayAge();//10
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 寄生组合继承的核心代码
* @param {Function} sub 子类
* @param {Function} parent 父类
*/
function inheritPrototype(sub, parent) {
// 拿到父类的原型
var prototype = Object(parent.prototype)
// 改变constructor指向
prototype.constructor = sub
// 父类原型赋给子类
sub.prototype = prototype
}
function Animal(species){
this.species = species
}
Animal.prototype.func = function(){
console.log('Animal')
}
function Cat(){
Animal.apply(this, arguments) // 只调用了1次构造函数
}
inheritPrototype(Cat, Animal)
/**
* 测试代码
*/
var cat = new Cat('cat')
cat.func() // output: Animal
console.log(cat.species) // output: cat
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
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
# new操作符干了什么
var obj = {};
obj.__proto__ = F.prototype;
F.call(obj);
1
2
3
2
3
- 第一行,创建一个空对象obj;
- 第二行,将这个空对象的__proto__成员指向了F函数对象prototype成员对象;
- 第三行,将F函数对象的this指针替换成obj,然后再调用F函数.
- 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象
以 new 操作符调用构造函数的时候,函数内部实际上发生以下变化:
- 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
- 属性和方法被加入到 this 引用的对象中。
- 新创建的对象由 this 所引用,最后隐式的返回 this.
具体实现:
function newOperator(ctor, ...args) {
if(typeof ctor !== 'function'){
throw 'newOperator function the first param must be a function';
}
let obj = Object.create(ctor.prototype);
let res = ctor.apply(obj, args);
let isObject = typeof res === 'object' && typeof res !== null;
let isFunction = typoof res === 'function';
return isObect || isFunction ? res : obj;
};
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# new 一个构造函数,如果函数返回 return {} 、 return null , return 1 , return true 会发生什么情况?
如果函数返回一个对象,那么new 这个函数调用返回这个函数的返回对象,否则返回 new 创建的新对象
← 事件流 执行上下文、作用域和作用域链 →