我理解的继承,是为了解决代码重复浪费空间与编写精力的问题,若有两个对象,面试
// person1
var person1={
name:'tom',
say(){
console.log(this.name);
}
}
// person2
var person2={
name:'jerry',
say(){
console.log(this.name);
}
}
复制代码
这两个对象都有相同的name
属性和say()
方法,只是name
属性值不一样,形成了代码的重复浪费,所以提出了节省代码的方式:数组
function person(name){
var obj=new Object();
obj.name=name;
obj.say=function(){
console.log(obj.name);
};
return obj;
}
var person1=person("tom");
var person2=person("jerry");
复制代码
function Person(name){
this.name=name;
this.say=function(){
console.log(this.name);
};
}
var person1=new Person("tom");
var person2=new Person("jerry");
复制代码
其实两种方式大同小异,由于在使用new操做符来建立一个实例对象时,产生了如下4个步骤:bash
var obj=new Object();
this
指向该对象,对该对象进行赋值obj.name='name
__proto__
指向构造函数的原型obj.__proto__=Person.prototype
这两种产生对象的方法,虽然节省了代码的书写量,但在内存上仍然消耗相同的空间,每建立一个新的实例对象仍然要建立新的属性和方法。因此就有了原型。函数
(1)首先,js里全部的函数都有一个prototype
属性,该属性是一个对象;同时js里面全部的对象(除去基本类型number,string,boolean,null和undefined
以外的全部)都有一个__proto__
属性,因此一个函数有prototype
和__proto__
两个属性,能够经过console.dir(fn)
查看。ui
function Person(name){
this.name=name;
}
console.dir(Person);
复制代码
prototype
里有个构造器
constructor
,指向的就是该构造函数,全部的对象都是由构造函数实例化获得的,如今咱们来看一下刚才讲new操做符时的第3个步骤:
__proto__
指向构造函数的原型obj.__proto__=Person.prototype
用图来表示就是:__proto__
属性都指向构造函数的
prototype
对象,因此能够把共享的方法写在
prototype
里,这样只须要建立一个方法就能够了。
function Person(name){
this.name=name;
}
Person.prototype.say=function(){
console.log(this.name);
}
var p1=new Person("tom");
p1.say(); // tom
var p1=new Person("jerry");
p1.say(); // jerry
复制代码
有了原型的概念,先给出原型链的概念:实例对象在使用属性或者调用方法时,若是本身没有,则会往上一级级查找prototype
对象,直到找到为止,若是最终也找不到则报错,就拿上面讲的,p1本身没有say方法,可是原型对象里面有该方法,因此能够调用。this
有了原型链的概念,咱们就能够实现继承了,即让子类构造函数的prototype
指向父类的一个实例对象。这样经过原型链的查找就能够继承到父类的方法,咱们一般须要继承的都是方法。spa
function Person(name){
this.name=name;
}
Person.prototype.say=function(){
console.log(this.name);
}
// 子类构造函数
function Student(name){
this.name=name;
}
// 将子类添加到原型链中
Student.prototype=new Person("tom");
// 子类本身的原型方法必须在改变原型指向后添加
Student.prototype.play=function(){
console.log(this.name+" play");
}
var s1=new Student("jerry");
s1.say(); // 原型链上的方法 jerry
s1.play(); // 本身原型上的方法 jerry play
// this一直指向都是s1,跟实例对象tom没有关系
复制代码
function Fn(){
// 实例方法,只能经过实例对象.的形式调用
this.work=function(){
console.log("work");
}
// 内部方法 只能内部调用
function learn(){}
};
// 静态方法,只能经过函数名.的形式调用
Fn.say=function(){
console.log("say")
}
// 原型方法,只能实例.的形式调用
Fn.prototype.play=function(){
console.log("play")
}
Fn.say(); // say
Fn.play(); // 报错
Fn.work(); // 报错
var f1=new Fn();
f1.say(); // 报错
f1.play(); // play
f1.work(); // work
复制代码
咱们可使用console.dir(Person)
查看一下:prototype
[1,2,3].isArray()
Array.splice.call(obj)
复制代码
完美搞错,真感谢那个面试小哥哥还耐心地给我讲解(捂脸羞愧)。
其实打印如下构造函数console,dir(Array)
就能够看到3d
isArray
是静态方法,
splice
是原型方法,因此正确的应该是:
Array.isArray([1,2,3]);
[].splice.call(obj); // []是Array的一个实例化对象
复制代码
l instanceof R 就是判断l的原型链上是否有R.prototypecode
s1 instanceof Student // true
s1 instanceof Person // true
复制代码
父类原型上的引用属性会被子类们共享,一个子类更改了,其他的也会被更改;
子类实例没法向父类构造函数传参
构造函数能够解决向父类构造函数传参的问题,但没有办法继承父类原型上的方法。
function Person(name){
this.name=name;
}
Person.prototype.say=function(){
console.log("say");
}
function Student(name,age){
Person.call(this,name);
this.age=age;
}
var s1=new Student("xixi",12);
s1.name; // xixi
s1.age; // 12
s1.say(); // 报错
复制代码
即便用构造函数来继承属性,使用原型来继承原型方法
function Person(name){
this.name=name;
}
Person.prototype.say=function(){
console.log("say");
}
function Student(name,age){
// 继承属性
Person.call(this,name);
this.age=age;
}
// 继承方法
Student.prototype=new Person();
var s1=new Student("xixi",12);
s1.name; // xixi
s1.age; // 12
s1.say(); // say
复制代码