JavaScript

| 从Array.prototype.slice.call()

2019-08-11  本文已影响0人  Hemingway_AT

最近在翻阅工具书,在基础温习章节,看到了一个比较长的方法调用。为巩固知识,提笔一记吧。目录如下:

一、prototype

prototype是Function的属性,常见的Object、Array等都是函数,确切地说是构造函数。

常见函数的类型.png
Function.prototype 属性存储了 [Function]的原型对象。[Function]对象继承自 Function.prototype。由此,“JavaScript基于原型”传唱至今。
函数的prototype属性.png
细心的你可以从上图中看到:prototype对象中包含一个constructor的键名。通过点语法,Object.prototype.constructor的返回值和Object等同。
console.log(Object.prototype.constructor === Object);
// expected output: "true"

到这里,自然要搬出来另一个家伙:__proto__。所有对象都有这个属性,它通过构造函数指向原型对象。

对象的__proto__属性.png
但是,Object.prototype.__proto__是不被鼓励的。

已废弃
该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性。

警告: 通过现代浏览器的操作属性的便利性,可以改变一个对象的 [[Prototype]] 属性, 这种行为在每一个JavaScript引擎和浏览器中都是一个非常慢且影响性能的操作,使用这种方式来改变和继承属性是对性能影响非常严重的,并且性能消耗的时间也不是简单的花费在 obj.__proto__ = ... 语句上, 它还会影响到所有继承来自该 [[Prototype]] 的对象,如果你关心性能,你就不应该在一个对象中修改它的 [[Prototype]]。相反, 创建一个新的且可以继承 [[Prototype]] 的对象,推荐使用 Object.create()

警告:Object.prototype.__proto__ 已被大多数浏览器厂商所支持的今天,其存在和确切行为仅在ECMAScript 2015规范中被标准化为传统功能,以确保Web浏览器的兼容性。为了更好的支持,建议只使用 Object.getPrototypeOf()

Object.prototype__proto__ 属性是一个访问器属性(一个getter函数和一个setter函数), 暴露了通过它访问的对象的内部[[Prototype]] (一个对象或 null)。
使用__proto__是有争议的,也不鼓励使用它。因为它从来没有被包括在EcmaScript语言规范中,但是现代浏览器都实现了它。__proto__属性已在ECMAScript 6语言规范中标准化,用于确保Web浏览器的兼容性,因此它未来将被支持。它已被不推荐使用, 现在更推荐使用Object.getPrototypeOf/Reflect.getPrototypeOfObject.setPrototypeOf/Reflect.setPrototypeOf(尽管如此,设置对象的[[Prototype]]是一个缓慢的操作,如果性能是一个问题,应该避免)。

上面的引用提到了一个性能的问题,对于要拓展一个对象,请不要随意修改其原型,使用Object.create()则是更优的选择,demo如下:

const person = {
  isHuman: false,
  printIntroduction: function () {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};

const me = Object.create(person);

me.name = "Matthew"; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten

me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"

二、slice

slice是Array实例具有的方法,实例则基于原型。slice() 方法可从已有的数组中返回选定的元素。用法参考:W3C

slice原生函数.png

三、call

call指的是Function.prototype.call()。call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数,与之类似的函数是Function.prototype.apply()。两者区别就是:call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。还有一个并列的函数叫Function.prototype.bind(),它则会创建一个新的函数;在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。

/** 构造函数 */
function Product(name, price) {
  this.name = name;
  this.price = price;
}

/** 扩展的构造函数 */
function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
  this.getCalorie = function(){
    return this.value;
  }
}

var food = new Food('cheese', 5);
console.log(food.name + ' is ' + food.price);
// expected output: "cheese is 5"

var globalArr = ['milk', 8];
Product.apply(this, globalArr);

console.log(window.name + ' is ' + window.price);
// expected output: "milk is 8"

var milkCalorie = {
  value: 100
};

console.log(new Food().getCalorie());
// expected output: "undefined"
console.log(new Food().getCalorie.bind(milkCalorie)());
// expected output: "100"

四、 回到标题

使用Array.prototype.slice.call()变个魔法,将“类似数组的对象”变成真正的数组。

image.png
同样地,一个可以替代字符串split方法的函数出现了:
console.log('future'.split(''))
// expected output: Array ["f", "u", "t", "u", "r", "e"]

console.log(Array.prototype.slice.call('future'))
// expected output: Array ["f", "u", "t", "u", "r", "e"]

(完)

上一篇 下一篇

猜你喜欢

热点阅读