最近在翻阅工具书,在基础温习章节,看到了一个比较长的方法调用。为巩固知识,提笔一记吧。目录如下:
- prototype
- slice
- call
一、prototype
prototype是Function的属性,常见的Object、Array等都是函数,确切地说是构造函数。
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__
。所有对象都有这个属性,它通过构造函数指向原型对象。
但是,
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.getPrototypeOf
和Object.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
三、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()
变个魔法,将“类似数组的对象”变成真正的数组。
同样地,一个可以替代字符串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"]
(完)
网友评论