// 第四版 Function.prototype.bind2 = function (context) {
var self = this; var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () { var bindArgs = Array.prototype.slice.call(arguments); return self.apply(thisinstanceof fNOP ? this : context, args.concat(bindArgs)); }
fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }
到此为止,大的问题都已经解决,给自己一个赞!o( ̄▽ ̄)d
三个小问题
接下来处理些小问题:
1.apply 这段代码跟 MDN 上的稍有不同
在 MDN 中文版讲 bind 的模拟实现时,apply 这里的代码是:
1
self.apply(thisinstanceof self ? this : context || this, args.concat(bindArgs))
多了一个关于 context 是否存在的判断,然而这个是错误的!
举个例子:
1 2 3 4 5 6 7 8 9 10 11
var value = 2; var foo = { value: 1, bar: bar.bind(null) };
functionbar() { console.log(this.value); }
foo.bar() // 2
以上代码正常情况下会打印 2,如果换成了 context || this,这段代码就会打印 1!
所以这里不应该进行 context 的判断,大家查看 MDN 同样内容的英文版,就不存在这个判断!
(2018 年 3 月 27 日更新,中文版已经改了😀)
2. 调用 bind 的不是函数咋办?
不行,我们要报错!
1 2 3
if (typeofthis !== "function") { thrownewError("Function.prototype.bind - what is trying to be bound is not callable"); }
3. 我要在线上用
那别忘了做个兼容:
1 2 3
Function.prototype.bind = Function.prototype.bind || function () { …… };
if (typeofthis !== "function") { thrownewError("Function.prototype.bind - what is trying to be bound is not callable"); }
var self = this; var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () { var bindArgs = Array.prototype.slice.call(arguments); return self.apply(thisinstanceof fNOP ? this : context, args.concat(bindArgs)); }
fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }
发表更新8 分钟读完 (大约1204个字)
JavaScript深入之call和apply的模拟实现
call
一句话介绍 call:
call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
举个例子:
1 2 3 4 5 6 7 8 9
var foo = { value: 1 };
functionbar() { console.log(this.value); }
bar.call(foo); // 1
注意两点:
call 改变了 this 的指向,指向到 foo
bar 函数执行了
模拟实现第一步
那么我们该怎么模拟实现这两个效果呢?
试想当调用 call 的时候,把 foo 对象改造成如下:
1 2 3 4 5 6 7 8
var foo = { value: 1, bar: function() { console.log(this.value) } };
Function.prototype.apply = function (context, arr) { var context = Object(context) || window; context.fn = this;
var result; if (!arr) { result = context.fn(); } else { var args = []; for (var i = 0, len = arr.length; i < len; i++) { args.push('arr[' + i + ']'); } result = eval('context.fn(' + args + ')') }
console.log(BigInt(999)); // 999n 注意:没有 new 关键字!!!
需要说明的是,BigInt 和 Number 是两种数据类型,不能直接进行四则运算,不过可以进行比较操作。
1 2 3
console.log(99n == 99); //true console.log(99n === 99); //false console.log(99n + 1);//TypeError: Cannot mix BigInt and other types, use explicit conversionss
GlobalThis
JS 中存在一个顶层对象,但是,顶层对象在各种实现里是不统一的。 从不同的 Javascript 环境中获取全局对象需要不同的语句,在 Web 中,可以通过 window、self取到全局对象,但是在 Web Workers 中,只有 self 可以。在 Node.js 中,它们都无法获取,必须使用 global。
在 globalThis 之前,我们这样去获取全局对象:
1 2 3 4 5 6
var getGlobal = function () { if (typeof self !== 'undefined') { return self; } if (typeofwindow !== 'undefined') { returnwindow; } if (typeofglobal !== 'undefined') { returnglobal; } thrownewError('unable to locate global object'); };
Types are further subclassified into ECMAScript language types and specification types.
An ECMAScript language type corresponds to values that are directly manipulated by an ECMAScript programmer using the ECMAScript language. The ECMAScript language types are Undefined, Null, Boolean, String, Number, and Object.
A specification type corresponds to meta-values that are used within algorithms to describe the semantics of ECMAScript language constructs and ECMAScript language types. The specification types are Reference, List, Completion, Property Descriptor, Property Identifier, Lexical Environment, and Environment Record.
因为 base value 是 EnvironmentRecord,并不是一个 Object 类型,还记得前面讲过的 base value 的取值可能吗? 只可能是 undefined, an Object, a Boolean, a String, a Number, 和 an environment record 中的一种。
IsPropertyReference(ref) 的结果为 false,进入下个判断:
2.2 如果 ref 是 Reference,并且 base value 值是 Environment Record, 那么 this 的值为 ImplicitThisValue(ref)
base value 正是 Environment Record,所以会调用 ImplicitThisValue(ref)
} // 虽然写在注释里,但是你要注意: // prototype是函数才会有的属性 Person.prototype.name = 'Kevin'; var person1 = new Person(); var person2 = new Person(); console.log(person1.name) // Kevin console.log(person2.name) // Kevin