关键词:继承,__proto__,prototype,constructor,instanceof,call(apply)。。。
(一)普通对象与函数对象
JavaScript 中,万物皆对象!但对象也是有区别的。分为普通对象和函数对象,
function F () {};
F是函数对象;Object 、Function、Date、Number等是 JS 自带的函数对象;
因为通过new Function()的方式进行创建的都是函数对象,包括Object 、Function也不例外。
var o1 = {}, o2 = new Object(), f = new F();
o1、o2、f为普通对象;
(二)constructor(构造函数属性)
constructor 属性是专门为 function 而设计,它存在于每一个 function 的prototype(原型对象) 属性中。这个 constructor 保存了指向 function 的一个引用。
在定义一个函数F时,JavaScript 内部会执行如下几个动作: 1.为函数F添加一个原形对象属性(即 F.prototype); 2. 为 原形对象 F.prototype 额外添加一个 constructor 属性(即 F.prototype.constructor),并且该属性保存指向函数 F 的一个引用;
即, F.prototype.constructor === F ;(true)
引申:Object.prototype.constructor === Object;Function.prototype.constructor == Function; Number.prototype.constructor == Number;
构造函数还有一层意义,f是通过new F()得到的对象,所以有f.constructor === F,即f的构造函数是F
(三)prototype(原型对象)
每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype 属性(上面说到),这个属性指向函数的原型对象。
即,F.prototype 就是F的原型对象
原型对象,顾名思义,它就是一个普通对象。
但有一个例外,Function.prototype 是一个函数对象,为啥?
可以这么理解,(二)中提到Function.prototype.constructor == Function;那么其实Function.prototype通过new Function()得到的,(一)中说到通过new Function()创建的都是函数对象。
Function.prototype也是一个例外,这个函数对象没有原型对象(prototype),因为Function.prototype是一个空函数。
Function.prototype也是唯一一个typeof XXX.prototype为 function的prototype
typeof F.prototype === 'Object' ;(true)
typeof Object.prototype === 'Object' ;(true)
typeof Number.prototype === 'Object' ;(true)
typeof Function.prototype === 'function' ;(true)
原型对象是用来做什么的呢?主要作用是用于继承。
// 定义一个类
function Person(name){
this.name = name || 'Person';
}
Person.prototype.getName = function(){
console.log(this.name);
}
//实例化
var person1 = new Person('Lee');
person1是Person的一个实例,那么person1就继承了Person的原型对象Person.prototype的属性
所以,person1.getName() ; //Lee
person1的构造函数是Person;
(四)__proto__(原型链)
JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__ 的内置属性,用于指向创建它的构造函数的原型对象。
所有函数对象的__proto__都指向Function.prototype,
person1.__proto__ === Person.prototype;(true)
Person.__proto__ === Function.prototype;(true)
Number.__proto__ === Function.prototype;(true)
Object.__proto__ === Function.prototype;(true)
Function.__proto__ === Function.prototype;(true)
所以原型链存在于实例person1与构造函数Person的原型对象Person.prototype之间,而不是存在于实例与构造函数之间,即(三)中说的person1继承了Person.prototype的getName方法;
Person.prototype.__proto__ 、Number.prototype.__proto__、Function.prototype.__proto__、Object.prototype.__proto__是什么?
Person.prototype、Number.prototype、Function.prototype都是普通对象,无需关注它有哪些属性,只要记住它是一个普通对象。普通对象的构造函数 === Object,所以,
Person.prototype.__proto__ === Object.prototype;(true)
Number.prototype.__proto__ === Object.prototype;(true)
Function.prototype.__proto__ === Object.prototype;(true)
Object.prototype 比较特殊,但它也有__proto__属性,为null。null处于原型链顶端(万物皆空)!!!!
上图:

(五)call(apply)
每个函数都包含两个非继承而来的方法:call()方法和apply()方法,作用是改变函数内部this的指向,两个方法作用是一样的,区别仅是传参方式不同。
var foo = {
name:"foo",
logName:function(){
console.log(this.name);
}
}
var bar={
name:"bar"
};
foo.logName();//foo
foo.logName.call(bar);//bar
call或者apply通过改变函数内部this的指向,可以帮助我们实现继承
(六)继承
组合继承:通过call(apply)调用父类构造函数,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用。举例说明:
// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
//实现继承
function Cat(name){
Animal.call(this,name);
// 子类扩展方法
this.climb = function(){
console.log(this.name + '会爬树!');
}
}
Cat.prototype = new Animal();//将父类实例作为子类原型,实现函数复用,这样Cat.prototype继承了Animal.prototype的所有方法
// 所以Cat的实例就继承了Animal.prototype的所有方法.
Cat.prototype.constructor = Cat;//修改构造函数属性(constructor)的指向,上面操作之后使得Cat.prototype成为了Animal的实例,
// 所以 Cat.prototype.constructor == Animal,但是原本在创造Cat的时候Cat.prototype.constructor === Cat,
// 所以这里修改回来,这样Cat的实例的构造函数才能指向Cat.
var cat = new Cat('Black Cat'); // new一个Cat的实例
console.log(cat.name);//Black Cat
cat.sleep();//Black Cat正在睡觉!
cat.eat('鱼');//Black Cat正在吃:鱼
cat.climb();//Black Cat会爬树!
console.log(cat.constructor === Cat);//true
(七)instanceof
instanceof就是JavaScript 中一个运算符,用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链
因为 cat.__proto__ === Cat.prototype
=> console.log(cat indstanceof Cat);//true
Cat.prototype是Animal一个实例 => Cat.prototype.__proto__ === Animal.prototype
=> console.log(cat indstanceof Animal);//true
Animal.prototype.__proto__ === Object.prototype
=> console.log(cat indstanceof Object);//true
然后 Object.prototype === null,到顶了