_ proto_
,隐式原型 对象特有(constructor
)
prototype
显式原型 函数特有
prototype
原型最大的作用,共享
属性和方法
js的原型是为了实现对象间的联系
,解决构造函数无法数据共享
而引入的一个属性。原型链
是实现对象间联系(继承)
的主要方法
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayHello=function(){
console.log(this.name + "say hello");
}
给函数Person的原型中声明了sayHello方法,当我们的构造实例对象去访问的时候访问的方法是同一个
var girl = new Person("bella",23);
var boy = new Person("alex",23);
console.log(girl.name); //bella
console.log(boy.name); //alex
console.log(girl.sayHello === boy.sayHello); //true
所有的引用类型,都具有对象特性,即可自由扩展属性(除了“null
”以外)
所有的引用类型,都有一个隐式原型__proto__
属性,属性值是一个普通的对象
所有的引用类型,隐式原型__proto__
属性值指向它的构造函数的显式原型“prototype
”属性值
当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型__proto__
(也就是它的构造函数的显式原型prototype
)中寻找。
instanceof
是通过原型去进行比较对象是否属于当前比较的构造函数
继承是通过原型链来体现的
function Person(){ }
var p1=new Person();
Person.prototype.name='老王';
Person.prototype.age='99';
console.log(p1.name);//'老王'
p1是Person实例化出来的函数,我并没有给p1定义name这个属性,那p1.name是怎么来的--是从Person.prototype来的,
因为p1._ proto_指向Person.prototype,当访问对象的某个属性时,现在这个对象本身去找,
如果找不到那就顺着_ proto_往上找,直到找到或者Object.prototype为止。
继承参考https://www.cnblogs.com/ranyonsue/p/11201730.html
组合继承(常用)
组合 原型链继承 和 借用构造函数继承
背后的思路是:使用原型链实现对原型方法的继承,而通过借用构造函数来实现对实例属性的继承。
Student.prototype = new Person()
Student.prototype.constructor = Student//组合继承也是需要修复构造函数指向的
function Parent(name){
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function(){
console.log(this.name);
}
function Child(name,age){
Parent.call(this,name);// 第二次调用 Parent()
this.age = age;
}
Child.prototype = new Parent(); // 第一次调用 Parent()
var child1 = new Child('xiaopao',18);
var child2 = new Child('lulu',19);
child1.getName(); // xiaopao
child2.getName(); // lulu
console.log(child1.age); // 18
console.log(child2.age); // 19
child1.colors.push('yellow');
console.log(child1.colors); // ["red", "blue", "green", "yellow"]
console.log(child2.colors); // ["red", "blue", "green"]
console.log(child1 instanceof Child); // true
console.log(child1 instanceof Parent); // true
优点:融合原型链继承和构造函数的优点,是JavaScript中最常用的继承模式
缺点:调用了两次父类构造函数
寄生组合式继承
子类构造函数复制父类的自身属性和方法,子类原型只接收父类的原型属性和方法
// 寄生组合式继承
function Parent(name){
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.sayName = function(){
console.log(this.name);
}
function Child(name,age){
Parent.call(this,name);
this.age = age;
}
function CreateObj(o){
function F(){};
F.prototype = o;
return new F();
}
// Child.prototype = new Parent(); // 这里换成下面
function prototype(child,parent){
var prototype = CreateObj(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
prototype(Child,Parent);
var child1 = new Child('xiaopao', 18);
console.log(child1);
普遍认为寄生组合式继承是引用类型最理想的继承方式
原型链继承
Student.prototype = new Person() // 子类型的原型为父类型的一个实例对象
子类的实例就可以通过__proto__访问到 Student.prototype 也就是Person的实例,
这样就可以访问到父类的私有方法,然后再通过__proto__指向父类的prototype就可以获得到父类原型上的方法
特点:
父类新增原型方法/原型属性,子类都能访问到
简单,易于实现
缺点:
无法实现多继承
来自原型对象的所有属性被所有实例共享
创建子类实例时,无法向父类构造函数传参
要想为子类新增属性和方法,必须要在Student.prototype = new Person() 之后执行,不能放到构造器中
借用构造函数继承
Person.call(this, name, age) // 相当于: this.Person(name, age)
特点:
解决了原型链继承中子类实例共享父类引用属性的问题
创建子类实例时,可以向父类传递参数
可以实现多继承(call多个父类对象)
缺点:
实例并不是父类的实例,只是子类的实例
只能继承父类的实例属性和方法,不能继承原型属性和方法
无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
组合继承优化
借助原型可以基于已有的对象来创建对象,var B = Object.create(A)以A对象为原型,生成了B对象。B继承了A的所有属性和方法。
function Person(name, age) {
this.name = name,
this.age = age
}
Person.prototype.setAge = function () {
console.log("111")
}
function Student(name, age, price) {
Person.call(this, name, age)
this.price = price
this.setScore = function () {}
}
Student.prototype = Object.create(Person.prototype)//核心代码
Student.prototype.constructor = Student//核心代码
var s1 = new Student('Tom', 20, 15000)
console.log(s1 instanceof Student, s1 instanceof Person) // true true
console.log(s1.constructor) //Student
console.log(s1)
ES6中class 的继承
ES5
的继承,实质是先创造子类的实例对象this
,然后再将父类的方法添加到this
上面(Parent.apply(this)
)。ES6
的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this
上面(所以必须先调用super
方法),然后再用子类的构造函数修改this
。
class
关键字只是原型
的语法糖,JavaScript继承
仍然是基于原型
实现的。
class Person {
//调用类的构造方法
constructor(name, age) {
this.name = name
this.age = age
}
//定义一般的方法
showName() {
console.log("调用父类的方法")
console.log(this.name, this.age);
}
}
let p1 = new Person('kobe', 39)
console.log(p1)
//定义一个子类
class Student extends Person {
constructor(name, age, salary) {
super(name, age)//通过super调用父类的构造方法
this.salary = salary
}
showName() {//在子类自身定义方法
console.log("调用子类的方法")
console.log(this.name, this.age, this.salary);
}
}
let s1 = new Student('wade', 38, 1000000000)
console.log(s1)
s1.showName()
并不是所有的浏览器都支持class关键字
网友评论