1.面向对象的三大特性
封装,继承,多态
1.1 原型链是面向对象的基础,
2.创建对象有几种方法
2.1 字面量
var obj11 = {name: 'smyh'};
var obj12 = new Object(name: `smyh`); //内置对象(内置的构造函数)
2.2 通过构造函数
var M = function (name) {
this.name = name;
}
var obj3 = new M('smyhvae');
2.3 Object.create
var p = {name:'smyhvae'};
var obj3 = Object.create(p); //此方法创建的对象,是用原型链连接的
3.原型、构造函数、实例,以及原型链
3.1原型、构造函数、实例三者之间的关系
1.构造函数通过 new 生成实例
2.构造函数也是函数,构造函数的prototype
指向原型。(所有的函数有prototype
属性,但实例没有 prototype
属性)
3.原型对象中有 constructor
,指向该原型的构造函数。
4.实例的__proto__
指向原型。也就是说,Foo.__proto__ === M.prototype
。
4.面向对象继承的几种方式
4.1方式一:借助构造函数
function Parent1() {
this.name = 'parent1 的属性';
}
function Child1() {
Parent1.call(this); //【重要】此处用 call 或 apply 都行:改变 this 的指向
this.type = 'child1 的属性';
}
console.log(new Child1);
【重要】上方代码中,最重要的那行代码:在子类的构造函数里写了Parent1.call(this);
,意思是:让Parent的构造函数在child的构造函数中执行。发生的变化是:改变this的指向,parent的实例 --> 改为指向child的实例。导致 parent的实例的属性挂在到了child的实例上,这就实现了继承。
这种方式,虽然改变了 this
的指向,但是,Child1 无法继承 Parent1
的原型。也就是说,如果我给 Parent1
的原型增加一个方法:
4.2方式二:通过原型链实现继承
function Parent() {
this.name = 'Parent 的属性';
}
function Child() {
this.type = 'Child 的属性';
}
Child.prototype = new Parent(); //【重要】
console.log(new Child());
重要】上方代码中,最重要的那行:每个函数都有prototype
属性,于是,构造函数也有这个属性,这个属性是一个对象。现在,我们把Parent
的实例赋值给了Child
的prototye
,从而实现继承。此时,Child
构造函数、Parent
的实例、Child
的实例构成一个三角关系。于是:
这种继承方式,Child 可以继承 Parent 的原型,但有个缺点:如果修改 child1实例的name属性,child2实例中的name属性也会跟着改变。
4.3方式三:组合的方式:构造函数 + 原型链
function Parent3() {
this.name = 'Parent 的属性';
this.arr = [1, 2, 3];
}
function Child3() {
Parent3.call(this); //【重要1】执行 parent方法
this.type = 'Child 的属性';
}
Child3.prototype = new Parent3(); //【重要2】第二次执行parent方法
var child = new Child3();
这种方式,能解决之前两种方式的问题:既可以继承父类原型的内容,也不会造成原型里属性的修改。
这种方式的缺点是:让父亲Parent
的构造方法执行了两次。
ES6
中的继承方式,一带而过即可,重点是要掌握ES5
中的继承。