对象继承
A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。这对于是非常有用的。
JavaScript 语言的继承不通过 class,而是通过“原型对象”prototype
实现
传统原型链式继承
- 过多的继承属性
- 比如一个函数用不到某个原型方法或属性,那么方法或属性就过剩了
1
2
3
4
5
6
7
8
9
10
11
12function Grand(){};
Grand.prototype.name="grand";
let grand = new Grand();
Father.prototype=grand;
function Father(){}
let father = new Father();
Son.prototype=father;
function Son(){}
let son = new Son();
借用构造函数 使用call/appply
- 不是真正继承,因为不能调用原型上的方法,而且每调用一次都会调用多次函数,实际上步骤没有变少
- 工业级推荐使用
- 缺点无法添加私有原型
1 | function Father() { } |
共有原型
1 |
|
圣杯模式
隐式附加的东西就私有化,可以公共定义的东西提取出来公有化
1
2
3
4
5
6
7
8
9
10
11 let inherit =(function(){
let Interim =function Interim() {};
return function (Target,Origin){//继承源
Interim.prototype =Object.create(Origin);
Target.prototype = Interim.prototype;
//现在 可以 制定自己的私有属性,但是 constuctor 不是 原函数所一手动赋值回来,如果想要知道函数真正继承那个原型需要保存它
Target.prototype.constuctor = Target;
Target.prototype.yliluokka =Origin;
}
}())
多重继承
JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过Object.assign
,实现这个功能。这种模式称之为 Mixin (混入)1
2
3
4
5
6
7
8
9
10
11
12function Fn1(){ }
function Fn2(){ }
function Son(){
F1.call(this);
F2.call(this);
}
//继承F1
Son.prototype =Object.create(Fn1.prototype);
//继承F2
Object.assign(Son.prototype,Fn2.prototype);
Son.prototype.constructor =Son;
let a =new Son();
call,apply and bind
都可改变函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。
call and apply
1 | function test() {} |
call and apply
二者区别在于传参:
- call 第二个参数开始单个单个参数传
- apply 第二个参数为数组或类数组
1
2
3
4
5
6//返回数组中最大的数
let a = [1, 2, 4, 1, 15];
Math.max.apply(null, a) // 15
//将数组的空元素变为undefined
Array.apply(null, [1,,3,,4])//[1,undefined,3,undefined,4];
空元素与undefined的差别
- forEach方法会跳过空元素,但是不会跳过undefined。因此,遍历内部元素的时候,会得到不同的结果。
1 | let obj={0: 1, length: 2} |
bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。
1 | let counter = { |
bind方法使用注意点
bind方法每运行一次,就返回一个新函数 需要一个变量接收
结合回调函数使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14let counter = {
count: 0,
inc: function () {
;
this.count++;
}
};
function callIt(callback) {
callback();
}
callIt(counter.inc.bind(counter));
counter.count // 1结合call方法使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20[1, 2, 3].slice(0, 1) // [1]
// 等同于
Array.prototype.slice.call([1, 2, 3], 0, 1) // [1]
//将Array.prototype.slice变成Function.prototype.call方法所在的对象
//调用时就变成了Array.prototype.slice.call。
let slice = Function.prototype.call.bind(Array.prototype.slice);
Function.prototype.slice.call([1, 2, 3], 0, 1) // [1]
//slice([1, 2, 3], 0, 1)
let push = Function.prototype.call.bind(Array.prototype.push);
let pop = Function.prototype.call.bind(Array.prototype.pop);
let a = [1 ,2 ,3];
push(a, 4)
a // [1, 2, 3, 4]
pop(a)
a // [1, 2, 3]将Function.prototype.bind方法变成Function.prototype.call的方法,就意味着bind的调用形式也可以被改写
1 | function f() { |
Object 系统默认方法
getPrototypeOf
获取对象原型,只有一个参数1
2
3
4
5
6
7
8
9
10function Foo (){}
let obj = new Foo ();
Object.getPrototypeOf(obj) // Foo.prototype
//空对象原型
Object.getPrototypeOf({}) // Object.prototype
// Object.prototype 原型
Object.getPrototypeOf(Object.prototype) //null
// Foo
Object.getPrototypeOf(Foo) // Function.prototypesetPrototypeOf
设置对象原型
有两个参数:- 现有对象
- 继承的原型对象
1
2
3
4
5let now = {};
let pro = {name:"Owen"};
Object.setPrototypeOf(now,pro);
now.name //"Owen"
Object.create()
生成实例对象的常用方法 参数必须为对象 或 null
- 参数为
null
会生成一个不会继承任何属性和方法的对象
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 let obj = Object.create(null);
obj.toString()// Error
//会继承第二个参数的属性和方法
let obj = Object.create({}, {
p1: {
value: 123,
enumerable: true,
configurable: true,
writable: true,
},
p2: {
value: 'Owen',
enumerable: true,
configurable: true,
writable: true,
}
});
// 等同于
let obj = Object.create({});
obj.p1 = 123;
obj.p2 = 'Owen';
//生成的对象会继承它的原型对象的构造函数。
function Foo() {}
let f = new Foo();
let b = Object.create(f);
b.constructor === Foo // true
b instanceof Foo // true
object.isPrototypeOf
判断对象是否再参数对象的原型链上
1 | function F(){} |
获取原型的三种方法
obj.__proto__
obj.constructor.prototype
Object.getPrototypeOf(obj)
前两种不可靠,都个一手动修改, 而且
__proto__
只有浏览器才需要部署getOwnPropertyNames
和keys
以数组形式返回参数对象所有属性名(不包含继承属性)
1
2
3
4//不管可不可遍历都会返回出来
Object.getOwnPropertyNames(Date);//["length", "name", "prototype", "now", "parse", "UTC"]
//返回可遍历属性
Object.keys(Date)// []
hasOwnProperty
判断参数是否是自身的属性,唯一一个不会遍历原型链的方法
1 Array.hasOwnProperty('length')//true
拷贝对象
拷贝对象需要确保两件事情:
- 与原对象具有同样的原型。
- 与原对象具有同样的实例属性。
1 |
|