前述:
什么是原型链?
由proto连起来的链条叫做原型链。当我们在对象中访问proto时,只有当原型链全部访问完毕,才会终止这一次的proto访问。如下图:
什么是_proto_?
基于js中的“万物皆对象”,proto是任何对象都有的属性,当然函数也会有(当函数做对象用时,比如new xxx);proto用来作指针,指向原型链的 下一个 prototype,是原型链的基本构成部分!
什么是prototype?
只有函数才有prototype属性。一般prototype属性放一些原型函数,比如toString、ofValue等等,这样可以避免创建实例的时候重复创建toString、ofValue等函数,节省内存空间。封装的原理从这也可以看出。
1、JS之封装
将要初始化的值放在函数中,经常用到的函数或者对象等放在prototype中。如
function Person (name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.sayHello = function(){
console.log('hello');
};
var per1 = new Person();
per1.sayHello(); //hello
这样我们就把这个sayHello的函数分装了,要调用的时候。直接per1.sayhello;并且这样做后,每创建一个实例对象,都不用再重新创建函数。因为这个sayHello函数已放在原型链(相当于一个总仓库)中,要调用的时候就直接从这调用,不用再创建。
2、JS之继承
所谓继承,就是让子类有父类的原型(prototype)。
根据上面的例子,我创建一个子类son
var son = function(){};
然后想直接继承person里的prototype(这就是JS继承)。
son .prototype = Person.prototype; //错误实现
这样只是简单的将son的原型指向了Person的原型,即son和Person使用了相同的原型,子类son增删改原型的方法,Person也会相应地变化,另外一个继承Person如anotherSon的类也会相应地变化。
所以我们需要将子类的proto指针指向父类的prototype。
son.prototype.proto= Person.prototype; //还是错误实现。
个人认为proto指针应指向地址而不是内容 或者 这里面只会将proto当做新添加的属性。
正确操作:
Object.create = function(o){
var F = function(){};
F.prototype = o; //这里只是铺垫。将父类的prototype直接给了实例F,这样看和上面无区别
return new F(); //这里是精髓。通过new一个实例对象,自然而然将_proto_指针指向o
};
son.prototype = Object.create(Person.prototype);
3、JS继承之es6的class类
es6新增的class类(需要在严格模式中运行)只是将上面的步骤简化,如
'use strict';
class Person{
constructor(str){
this.content = str;
}
sayHello(){
return this.content;
}
//添加了static静态函数关键字
static concat(str1, str2){
return str1 + str2;
}
}
//extends继承关键字
class son extends Person{
constructor(str){
//使用super调用父类的方法
super(str);
}
}
var str1 = new Person("hello"),
str2 = new Person(" world");
console.log(str1); //输出Person{content: "hello"}
console.log(str1.content); //输出hello
console.log(str1.sayHello()); //输出hello
console.log(Person.concat(str1, str2));//输出hello world
var ustr = new son("ustring");
console.log(ustr); //输出Person{content: "ustring"}
console.log(ustr.sayHello()); //输出ustring