创建对象
首先来说一下创建对象的几种方法:
- 字面量的时候,当前对象原型链. -默认指向Object。
var o1 = {name: 'o1'}; // 字面量对象
var o2 = new Object({name: 'o2'}) // 通过new Object声明一个对象
注:可以把Object
当成一个构造函数,可以使用new
运算符来创建一个对象。
看一下打印出来的o1,o2:
- 通过显示的构造函数来创建对象。
var M = function() {this.name = 'o3'};
var o3 = new M();
看一下打印出来的o3:
- Object.create方法创建对象.
var p = {name: 'o4'};
var o4 = Object.create(p);
console.log(o4)
说明:
- Object.create();创建的对象是用原型链来连接的,o4.__proto__指向的是p对象
- Object.create()创建的本身就是空对象,空对象是没有name属性的,name属性是在原型对象上的。
看一下打印出来的o4:
原型、构造函数、实例、原型链之间的关系
- 任何一个函数只要被new使用了,这个函数就可以称为构造函数,new 的作用是对当前对象的this不停的赋值,如果没有new,this会指向window,但是原型指向不变。构造函数可以通过new运算符生成一个实例。
- 声明一个函数的时候,js引擎会给这个构造函数自动加上原型对象,即prototype,此时这个对象是个空对象。
- constructor用于区分原型对象被哪个构造函数所引用,原型对象中会有一个构造器(constructor),这个构造器会默认声明的那个函数。
原型链的工作原理: 通过原型链的方式找到原型对象,原型对象被不同的实例所共有的。如果在当前实例的构造函数内部找不到方法或属性,就会通过原型链往上层的原型对象__proto__去找,如果找不到,继续找,直到找到为止。如果找到Object. prototype还没找到的话,就会原路返回,告诉它没有这个方法。
-
函数有prototype,对象是没有prototype的。
-
只有实例对象有__proto__ , 函数也是一个对象,所以也会有__proto__
M.__proto__ === Function.prototype // true
M的这个普通函数,是Function这个构造函数的一个实例。
- 修改实例的原型对象,实际上就是修改了构造函数的原型对象
上图:
总结:
- 所有的引用类型(数组、对象、函数),都具有对象的特性,即可自由扩展属性(除了'null'以外)
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100;
function fn() {};
fn.a = 100;
- 所有的引用类型(数组、对象、函数),都有一个
__proto__
(隐式原型)属性,属性值是一个普通的对象。
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
- 所有的函数,都有一个
prototype
(显式原型)属性,属性值也是一个普通对象。
console.log(fn.prototype);
- 所有的引用类型(数组、对象、函数),
__proto__
属性值指向它的构造函数的prototype
属性值。
console.log(obj.__proto__ === Object.prototype);
- 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中去找。
instanceof原理
instanceof 是用于判断一个实例是否属于某种类型。
其实就是判断实例对象__proto__这个属性与构造函数的prototype是不是同一个引用地址。只要是在这条原型链上的构造函数,实例对象instanceof 构造函数,都是true,如下:
o3 instanceof Object // true
o3.__proto__ === M.prototype // true
M.prototype.__proto__ === Object.prototype // true
假如要判断实例真正的构造函数是谁的时候,就需要用到constructor
o3.__proto__.constructor === M // true
o3. __proto__.constructor === Object // false
new 运算符
- 一个新对象被创建。它继承foo.prototype.
- 构造函数foo被执行。执行的时候,相应的传参会被传入,同时上下文(this)会被指定为这个新实例。new foo等同于new foo(),只能用在不传递任何参数的情况。
- 如果构造函数返回了一个“对象”,那么这个对象会取代整个new出来的结果。如果构造函数没有返回对象,那么new出来的结果为步骤一创建的对象。
var new2 = function(func) {
var o = Object.create(func.prototype);
var k = func.call(o);
if(typeof k === 'object') {
return k;
} else {
return o;
}
}
类与实例
类的声明
// 传统声明
function Animal() {
this.name = ‘name’;
}
// es6中的class声明
class Animal2 {
constructor() {
this.name = name;
}
}
生成的实例(实例化)
new Animal();
new Animal2;
类与继承
如何实现继承?(本质就是原型链)
- 借助构造函数实现继承(构造函数继承)
将父级构造函数运行时的this指向子构造函数的实例上。所以父级的一些属性和方法,子类也有。
缺点:父类的原型对象上的方法并没有被子类继承,导致并没有实现真正的继承,只实现了部分继承。
function Parent1() {
this.name = 'parent1';
}
functin Child1() {
Parent1.call(this);
thhis.type = 'child1';
}
console.log(new Child1);
- 借助原型链实现继承(类式继承)
通过原型链继承。父类的实例赋值给子类的原型。
缺点: 原型链的原型对象是公用的,所以造成共有的属性直接影响到其他的子类。子类实例化时,无法向父类传参,因此在实例化父类的时候也无法对父类构造函数内的属性进行初始化。
function Parent2() {
this.name = 'parent2';
}
functin Child2() {
thhis.type = 'child2';
}
Child2.prototype = new Parent2();
console.log(new Child2());
- 组合继承 (类式继承+构造函数继承)
满足类式继承和构造函数继承的缺点,子类实例更改父类继承下来的引用类型属性,根本不会影响到其他实例,并且子类实例化过程中能将参数传递到父类的构造函数中。
缺点:父类构造函数调用了两遍。在使用构造函数继承时执行了一遍父类的构造函数,在实现子类原型的类式继承时有调用了一遍父类的构造函数。
function Parent3() {
this.name = 'parent3';
this.play = [1, 2, 3];
}
functin Child3() {
Parent3.call(this);
thhis.type = 'child3';
}
Child3.prototype = new Parent3();
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play);
- 组合继承优化1
存在缺点:子类的constructor指向的是父类本身,而不是自己本身。所以子类的实例化是由父类直接实例化,而不是自身。同一个constructor无法区分实例是由父类创建的还是子类创建的,因为引用的是同一个对象
function Parent4() {
this.name = 'parent4';
this.play = [1, 2, 3];
}
functin Child4() {
Parent4.call(this);
thhis.type = 'child4';
}
Child4.prototype = Parent4.protype;
var s5 = new Child4();
var s6 = new Child4();
s5.play.push(4);
console.log(s5.play, s6.play);
- 组合继承优化2(组合继承的最优写法)
原理:通过Object.create()创建一个中间对象,创建的本身是一个空对象,做到父类和子类的原型对象区分开,创建出来的对象是用原型链连接的.
Object.create(参数)创建出来的对象的原型对象就等于参数。
function Parent5() {
this.name = 'parent5';
this.play = [1, 2, 3];
}
functin Child5() {
Parent5.call(this);
thhis.type = 'child5';
}
Child5.prototype = Object.create(Parent5.protype);
Child5.prototype.constructor = Child5;
var s7 = new Child5();
console.log(s7);