JS原型对象

什么是原型对象


在 JS 中函数拥有一个prototype属性,这个属性指向一个对象,称之为原型对象,原型对象又拥有一个constructor属性,指向它的函数.

如何创建原型对象


// 创建了一个构造函数
function Girl(name, age) {
  this.name = name;
  this.age = age;
}
// 创建了一个对象
const girlFriend = new Girl("无", "-1");

image girlFriend

可以看到在DevTools中显示该对象上有一个[[Prototype]]属性,点开[[Prototype]]显示的就是girlFriend.__proto__的属性,指向的是girlFriend的构造函数(Girl)的原型对象

!. 在谷歌浏览器的 DevTools 中显示的[[ProtoType]]就是指__proto__

image prototype

!. 火狐浏览器 DevTools 中显示的是<prototype>,同样指的是__proto__

image firefox

!. 主流浏览器环境中的__proto__是 ES 标准中[[Prototype]]的实现,在其他 ES 实现环境中可能就不存在__proto__这个属性,如Node

我们接着往下看,点开 girlFriend 对象的[[Prototype]](girlFriend.__proto__)后又有一个[[Prototype]]属性,这个属性指向的是Object构造函数的原型对象

image obj

原因很简单,Girl的原型对象是一个Object实例,它是由Object构造函数创建的,所以它指向Object构造函数的原型对象

可以看到这个原型对象上没有了[[Prototype]]属性,那么这个原型对象的原型对象就是null

!. 上面那张图片上有一个__proto__,但它并不是我们前面说的在浏览器环境中实现的__proto__属性,它是一个getter函数的返回值,它和原型和原型链没有关系,它的作用涉及到浏览器内部原型链的构建,我们作为开发人员不需要关注__proto__这个属性,后面会详细介绍

阅读了上面,我们从一个对象出发,访问到一个原型对象,又从这个原型对象上访问到另一个原型对象,是不是就像构成了一条链子,我们称之为原型链

原型链


上面的例子会形成一个原型图,在这个原型图中有三条原型链:

image lian

  • prototype这个属性只存在于函数上
  • __proto__指向该对象的构造函数的原型对象
  • Function构造函数是由自己构造的,所以其__proto__指向Function的原型对象
  • Object是一个函数,它的构造函数是Function,所以指向Function的原型对象
  • 原型对象是一个Obejct对象,除Object的原型对象外,都指向Obejct的原型对象
  • Objcet的原型对象的__proto__指向NULL,是原型链的终点

继承(原型的作用)


原型链继承

// 创建一个构造函数
function Girl(name, age) {
  this.name = name;
  this.age = age;
}

// 创建一个构造函数
function myGirl(name) {
  this.name = name;
}

// 将myGirl构造函数的原型指向一个Girl实例
myGirl.prototype = new Girl("武则天", 18);

const undefinedGirl = new myGirl("无");

console.log(undefinedGirl.name); // '无'
console.log(undefinedGirl.age); // 18

通过上述代码我们可以发现,在myGirl上我并没有定义age属性,但却可以访问到,是因为:

当我们访问对象的属性或方法时,JavaScript 引擎会沿着原型链查找,直到找到相应的属性或方法或者达到原型链的末端(null)

我们没有在myGirl上定义age属性,我们通过设置myGirlprototype(原型对象),实现了在myGirl上可以访问到Girl上的属性,即实现了继承

缺点

因为继承的原型对象为同一个,若其中一个继承了该原型对象的实例对象修改了原型上的属性,其他实例对象均会受到影响

构造函数继承

// 定义一个函数
function Girl(name) {
  this.name = name;
}

// 设置该构造函数的原型
Girl.prototype.sayHello = function () {
  console.log(`I am ${this.name}`);
};

// 定义一个构造函数
function myGirl(name, love) {
  // 调用Girl方法并将其this指向为由该构造函数创建的实例对象
  Girl.call(this, name);
  // 子类特有的属性
  this.love = love;
}

// 创建对象
const girlFriend = new myGirl("myself", true);

girlFriend.sayHello(); // Uncaught TypeError: girlFriend.sayHello is not a function

缺点

  1. 这种继承方式只能继承父类中的属性和方法,无法继承父类原型中的属性和方法
  2. 无法实现复用

组合继承

组合继承就是上面俩种方式的结合,用原型链实现对父类原型属性和方法的继承,用构造函数继承的思想来实现父类的属性和方法的继承

// 定义一个函数
function Girl(name) {
  this.name = name;
}

// 设置该构造函数的原型
Girl.prototype.sayHello = function () {
  console.log(`I am ${this.name}`);
};

// 定义一个构造函数
function myGirl(name, love) {
  // 调用Girl方法并将其this指向为由该构造函数创建的实例对象
  Girl.call(this, name);
  // 子类特有的属性
  this.love = love;
}

// 将子类的原型指向父类的实例对象
myGirl.prototype = new Girl();
// 设置constructor属性,指向子类构造函数
myGirl.prototype.constructor = myGirl;

// 创建对象
const girlFriend = new myGirl("丁真", true);

const mistress = new myGirl("雪豹", false);

girlFriend.sayHello(); // I am 丁真
mistress.sayHello(); // I am 雪豹

缺点

子类的原型上会包含父类的实例,导致原型链上存在多余的属性和方法

原型式继承

通过一个已有的对象创建一个新的对象,新对象的原型链上包含了原始对象的引用

function createGirlFriend(obj) {
  function F() {}
  F.prototype = obj;
  return new F();

  // 可直接用Object.create(obj, ...options);
}

const girl = {
  name: "蔡徐坤",
  say: function () {
    console.log(`my name is ${this.name}`);
  },
};

const myGirlFriend = createGirlFriend(girl);

myGirlFriend.name = "007";

myGirlFriend.say(); // my name is 007

缺点

原型式继承不支持像构造函数继承那样传递参数给父对象的构造函数。

寄生式继承

和原型式继承几乎没有区别

function createGirlFriend(obj) {
  function F() {}
  F.prototype = obj;
  // 新对象继承原对象的属性,并在新对象上添加额外的方法
  F.method = function () {
    // .....
  };
  return new F();
  // 可直接用Object.create(obj, ...options);

  // ....
  // ....
}

区别在于寄生式继承额外在新对象上添加了一些方法或属性,而原型式继承则更侧重于复制对象的结构

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值