关于JS原型、原型链和继承的问题

  声明一个对象可以var obj = {},这是字面意思;或者var obj = new Object(),Object为一个构造函数,是指创建一个空对象,这个空对象把Object.prototype(Object的原型对象)克隆了过来,用自己的__proto__储存了起来,obj称为Object的实例。两者的本质是一样的,同样都声明了一个新对象。所有新对象都是克隆了其构造函数的原型对象到自身的__proto__属性身上。通过对象的__proto__无法修改构造函数的原型prototype,但是通过prototype可以修改其实例化对象的原型__proto__。

一、原型

  当我们需要创建多个对象时,每创建一个对象就要给这个新的对象添加属性或方法,尽管有的属性或方法是相同的。这个时候我们可以把共有的属性方法写到构造函数的原型对象(prototype)上,每创建多个具有相同属性的对象时,克隆构造函数的原型对象就好。每个构造函数都有自己的原型对象每个对象也有自己的原型(__proto__属性),__proto__也是一个对象,克隆自构造函数的原型对象

 例1:
复制代码
 1 function Person(name){
 2   this.name = name;
 3 }
 4 Person.prototype.say = function() {
 5   //定义构造函数的原型。注意:要写在实例化之前
 6    console.log("I am" + this.name);
 7 }
 8 var p1 = new Person("xiaoming");
 9 var p2 = new Person("xiaowang");
10 console.log(p1.name)  //xiaoming;
11 console.log(p2.name) //xiaowang;
12 p1.say(); //I am xiaomin
13 p2.say() ;//I am xiaowang
复制代码

此时p1 = {},p2 = {},没有任何属性和方法,却可以访问say()方法,这是因为p1,p2身上的隐式属性__proto__克隆了构造函数的原型Person.prototype,前面我们给Person.prototype对象添加了say()方法。当我们p1.say()调用时,自身没有会去自己的__proto__上查找,所以p1,p2能够访问say()方法。

new关键字做了什么?

1.创建一个全新的空对象,并从其构造函数原型身上复制属性和方法到自己的原型__proto__身上;

2.执行构造函数,把构造函数的内部的this指向创建完的新对象;

3.构造函数默认返回这个对象

重写new关键字:

复制代码
function Person(name){
  this.name = name;
}
Person.prototype.say = function() { 
  console.log("I am" + this.name);
}
function _new(){
  var Foo = [].shift.call(arguments)
  var obj = Object.create(Foo.prototype)
  Foo.apply(obj,arguments)
  return obj
}
var p3 = _new(Person,'xiaoming')
复制代码

二、原型链

  每个构造函数都有自己的原型prototype,而每个对象则有自己的原型__proto__=== 其构造函数的prototype,prototype也是一个对象,是对象就有自己的构造函数,也有自己的原型__proto__(等同于构造函数的prototype)。当我们使用对象上的属性时,首先会从自己身上查找,自身没有会去自己的原型__proto__身上找,__proto__上没有会去__proto__.__proto__上找.....最终会找到Object.prototype,这个关系就叫原型链。通过测试我们可发现:

1 p1.__proto__ === Person.prototype    //true
2 Person.prototype.__proto__ === Object.prototype   //true
3 Object.prototype.__proto__ === null   //true

 

三、继承

  当不同的对象身上有着不同的方法和属性时,如果我们希望在此对象使用彼对象的属性和方法又该怎么做?

  1、通过new实现原型链继承

复制代码
function SuperType() {
  this.name = 'super';
  this.colors = ["red", "blue", "green"];
}
function SubType(name){
  this.name = name;
}
//继承了 SuperType SubType.prototype = new SuperType();
var sub1 = new SubType('sub1'); var sub2 = new SubType('sub2'); sub1.colors.push("black"); console.log(sub1.name); //sub1 console.log(sub2.name); //sub2 console.log(sub1.colors); //"red,blue,green,black" console.log(sub2.colors); //
"red,blue,green,black"
复制代码
  • 优点
    实现方式简洁,父类新增原型属性和方法,子类都能访问到。
  • 缺点
    (1) 原型属性在所有实例中是共享的,修改任意实例属性会影响其他实例属性的值。
    (2) 在创建子类型的实例sub1时,不能向超类型的构造函数SuperType中传递参数。实际上, 应该说是没有办法在不影响所有对象实例的情况下,给SuperType传递参数。
    (3)子类实例原型上同样也克隆了SuperType的私有属性this.name,这是不必要的,因为SubType也有自己的this.name。

   2、Object.create实现继承

复制代码
function Father(){
  this.name = 'father';
}

Father.prototype.say = function(){
  console.log('say hello')
}
function Son(){}
Son.prototype = Object.create(Father.prototype, {name: {value: 'son'}});
var son = new Son();
son.say();//"say hello'
复制代码
  • 优点
    实现方式简洁,父类新增原型属性和方法,子类都能访问到。不会克隆父类的name属性到子类实例的原型链上。
  • 缺点
    (1) 原型属性在所有实例中是共享的,修改任意实例属性会影响其他实例属性的值。
    (2) 在创建子类型的实例时,不能向父类传递参数。

  3、组合继承

复制代码
function SuperType(name) {
     this.name = name;
     this.colors = ["red", "blue", "green"];
}  
SuperType.prototype.sayName = function() {console.log(this.name);};  
function SubType(name, age) {    
     //借助构造函数继承属性
     SuperType.call(this, name); //调用一次 SuperType 构造函数
     this.age = age;
 }  
//原型链方式继承 sayName 方法
SubType.prototype = new SuperType(); //再次调用 SuperType 构造函数
SubType.prototype.constructor = SubType; //将 constructor指回 SubType
SubType.prototype.sayAge = function() {console.log(this.age);};  
var sub1 = new SubType("Nicholas", 29);
var sub2 = new SubType("Greg", 27);
sub1.colors.push("black");

console.log(sub1.colors); //"red,blue,green,black"
sub1.sayName();           //"Nicholas";
sub1.sayAge();            //29  
console.log(sub2.colors); //"red,blue,green"
sub2.sayName();           //"Greg";
sub2.sayAge();            //27  
复制代码
  • 优点
    使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性
  • 缺点
    SubType的构造函数会被调用两次

 

posted @   傻白有点甜  阅读(429)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示