前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >再看JavaScript,那些遗漏或易混淆的知识点(4)

再看JavaScript,那些遗漏或易混淆的知识点(4)

作者头像
踏浪
发布2021-12-24 11:07:38
2770
发布2021-12-24 11:07:38
举报
文章被收录于专栏:踏浪的文章踏浪的文章

原型与继承

原型继承就是可以使一个对象可以使用另一个对象上面的某一些属性,要求是这个对象没有这个属性。如果有这个属性,就直接使用自己的了(访问器属性除外)。

代码语言:javascript
复制
let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

比如上面的两个对象, animalrabbit ,我想要 rabbit 可以使用 eats 这个属性。不再改变 rabbit 的情况下怎么做呢?方法有很多,我们一个个来看看。

[[Prototype]]

这个属性是 JavaScript 的一个隐藏属性,他的值只能有两种情况, null 或者是另一个对象的引用 。注意了,这里是引用,而不是拷贝,对象的引用容易出现的问题这里就不多少了。

代码语言:javascript
复制
let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

console.log(rabbit.eats);
rabbit.__proto__ = animal; // 设置 rabbit.[[Prototype]] = animal'
console.log(rabbit.eats);

// 因为对象的引用,导致 animal 多了一个属性 name
rabbit.__proto__.name = "jack";

console.log(animal);

JavaScript 原型与继承

在这儿我们可以说 “animal 是 rabbit 的原型”,或者说 “rabbit 的原型是从 animal 继承而来的”。

因此,如果 animal 有许多有用的属性和方法,那么它们将自动地变为在 rabbit 中可用。这种属性被称为“继承”。

代码语言:javascript
复制
let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

console.log(rabbit.eats);
rabbit.__proto__ = animal; // 设置 rabbit.[[Prototype]] = animal'
console.log(rabbit.eats);

// 因为对象的引用,导致 animal 多了一个属性 name
rabbit.__proto__.name = "jack";

console.log(animal);

// 很长很长的原型链
let longer = {
  longer: 10,
  __proto__: rabbit
};

console.log(longer.eats);

这里只有两个限制:

  1. 引用不能形成闭环。如果我们试图在一个闭环中分配 __proto__,JavaScript 会抛出错误。
  2. __proto__ 的值可以是对象,也可以是 null。而其他的类型都会被忽略。

注意__proto__[[Prototype]] 的因历史原因而留下来的 getter/setter。它们两个本质上是不一样的。不过__proto__ 的确是有些过时了。现在我们一般使用 Object.getPrototypeOf/Object.setPrototypeOf 来取代 __proto__ 去 get/set 原型。不过两者都能用,而且 __proto__ 更简便一些。

在对象上添加原型上面已有的属性

代码语言:javascript
复制
let animal = {
  eats: true,
  walk() {
    /* rabbit 不会使用此方法 */
  }
};

let rabbit = {
  __proto__: animal
};

rabbit.walk = function() {
  alert("Rabbit! Bounce-bounce!");
};

rabbit.walk(); // Rabbit! Bounce-bounce!

原型 animal 上又一个 walk 函数,对象 rabbit 的原型是继承于 animal 。 现在在 rabbit 上添加一个 名字为 walk 的函数,此时在调用这个函数。就不会在原型上去找,因为自身就有这个方法。

但是,访问器(get/set)属性是一个例外。

代码语言:javascript
复制
let user = {
  name: "John",
  surname: "Smith",

  set fullName(value) {
    [this.name, this.surname] = value.split(" ");
  },

  get fullName() {
    return `${this.name} ${this.surname}`;
  }
};

let admin = {
  __proto__: user,
  isAdmin: true
};

alert(admin.fullName); // John Smith

// setter triggers!
admin.fullName = "Alice Cooper";

alert(admin.fullName); // Alice Cooper,admin 的内容被修改了
alert(user.fullName);  // John Smith,user 的内容被保护了

如果对象上面添加的属性是原型的设置的访问器属性,那么这个对象上面的属性就会作用于原型上,直接调用原型的getter/setter

注意最后面的两行代码。 adminuserfullName 是不同的。那为什么回不同呢?原因就是因为访问器属性中的 this这里始终记住一点: this 的指向始终指向 . 符号前面的对象。简单来说就是谁调用那么就指向谁。(箭头函数(访问器属性不能使用箭头函数)与使用了call,apply,bind的函数除外)。

遍历对象

遍历对象这里说说 for...in 循环与 Object.keys 的区别。

代码语言:javascript
复制
let animal = {
  eats: true
};

let rabbit = {
  jumps: true,
  __proto__: animal
};

// Object.keys 只返回自己的 key
console.log(Object.keys(rabbit)); // jumps

// for..in 会遍历自己以及继承的键
for(let prop in rabbit) console.log(prop); // jumps,然后是 eats

区别就如上面的代码注释中的那样。所以,在 Object.keys 方法出现以前,我们都需要使用 obj.hasOwnProperty(key) 来进行判断。所有的对象都有 hasOwnProperty 属性,都是从 Object 对象上面继承的哦

Function.prototype

代码语言:javascript
复制
function Fn() {}
console.log(Fn.prototype); // { constructor: Fn() {} }
console.log(Fn.prototype.constructor); // FN() {}
const fn = new Fn();
console.log(fn.__proto__); // { constructor: Fn() {} }

每一个函数都有一个 prototype 属性,这个属性的值**不一定**等于 {constructor: XXX() {}} 。因为我们可以手动修改它。

代码语言:javascript
复制
Fn.prototype = {name: 'Jack'};

所以我们不要这样去写,而是在 prototype 上面去添加属性

代码语言:javascript
复制
Fn.prototype.name = 'Jack';

或者修改了以后重新指定它的 constructor

代码语言:javascript
复制
Fn.prototype = {
	constructor: Fn,
	name: 'Jack'
};

构造函数的 prototype 值是 { constructor: Fn() {} } 。构造函数的实例值也是 { constructor: Fn() {} }。但是两者是不一样的(内存地址不同吧)。但是他们的 constructor 是一样的,都是指向这个构造函数。

代码语言:javascript
复制
function Fn() {}
let fn = new Fn();
console.log(Fn.prototype === fn.__prpto__) // false
console.log(Fn.prototype.constructor === fn.constructor) // true

所以,当我们不知道某个对象的构造函数的时候,可以使用 constructor 来创建(注意没有被修改)。

代码语言:javascript
复制
function Rabbit(name) {
  this.name = name;
  alert(name);
}

let rabbit = new Rabbit("White Rabbit");

let rabbit2 = new rabbit.constructor("Black Rabbit");

JS原型链有一个很经典的图

https://pic-go-1253455210.cos.ap-chengdu.myqcloud.com/blog/JS%E5%8E%9F%E5%9E%8B%E9%93%BE.jpg
https://pic-go-1253455210.cos.ap-chengdu.myqcloud.com/blog/JS%E5%8E%9F%E5%9E%8B%E9%93%BE.jpg
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-06-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 原型与继承
  • [[Prototype]]
  • 在对象上添加原型上面已有的属性
  • 遍历对象
  • Function.prototype
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档