Function.prototype.bind及其polyfill分析
来源:互联网 发布:linux run parts 编辑:程序博客网 时间:2024/06/05 09:37
Function.prototype.bind
执行会返回一个新的函数,并将this关键字设置为指定的值。并可以在执行该返回的函数之前传入参数。
语法
fun.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg
:表示返回的函数中this的指向。[, arg1[, arg2[, ...]]]
:表明参数是可选的。
使用方法
绑定this
var obj = { name: 'real', getName: function() { console.log(this.name) }}var name = 'window';obj.getName(); // "real"var getName = obj.getName;getName(); // "window";var getName2 = obj.getName.bind(obj);getName2(); // "real";
上述getName2
方法中this
指向了obj
。
偏函数(提前传入参数)
var log = function log(...args) { console.log(args);}log(1,2,3); // [1, 2, 3]var log2 = log.bind(null, 0);log2(1,2,3); // [0, 1, 2, 3]
提前传入函数0。
结合setTimeout
setTimeout(fn, delay)
。其中fn
中的this
默认指向的是全局window/global
。
function Foo(name){ this.name = name; };Foo.prototype.getName = function(){ console.log(this.name); }Foo.prototype.declare = function() { setTimeout(this.getName, 100); }var name = 'window';var foo = new Foo('real');foo.declare(); // 'window'
setTimeout(this.getName, 100);
中的this
指向的foo
对象,可以找到Foo.prototype.getName
。没能执行预期结果是因为Foo.prototype.getName
中的this
指向了window
。如果改成:
setTimeout(this.getName.bind(this), 100);
可以取得预期效果,打印real
。
结合new
这里大致说一下。你不知道的javascript 上卷一书中提到this
有四种绑定方式。
① 默认绑定到window
var name = 'real';console.log(this.name);
②隐式绑定到所在对象
var obj = { name: 'real', getName: function() { return this.name; }}console.log(obj.getName()) // 'real';
③显示绑定Function.prototype.apply/call
及其变种Function.prototype.bind
④new
绑定。
function Foo(name) { this.name = name; }var foo = new Foo('real');console.log(foo.name); // 'real'
其中,优先级从①到④依次增高。
和Function.prototype.apply
/call
区别
区别在于Function.prototype.bind
不会执行被绑定的函数,并且返回一个新的函数。而Function.prototype.apply
/call
会执行被绑定的函数,并且被绑定的函数中的this
发生偏移,并且传入参数。
polyfill的理解
bind 函数在 ECMA-262 第五版(es5)才被加入;它可能无法在所有浏览器上运行。你可以部分地在脚本开头加入以下代码,就能使它运作,让不支持的浏览器也能使用 bind() 功能。
if (!Function.prototype.bind) { Function.prototype.bind = function(oThis) { if (typeof this !== 'function') { // closest thing possible to the ECMAScript 5 // internal IsCallable function throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function() {}, fBound = function() { return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; if (this.prototype) { // Function.prototype doesn't have a prototype property fNOP.prototype = this.prototype; } fBound.prototype = new fNOP(); return fBound; };}
以一个demo为例。
function foo(name) { this.name = name; } var obj = { name: 'real'};var bar = foo.bind(obj);bar('123'); // 注1console.log(obj.name); // 123var baz = new bar('abc'); // 注2console.log(baz.name); // 'abc'
接下来逐步分析。
if (typeof this !== 'function') { // closest thing possible to the ECMAScript 5 // internal IsCallable function throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); }
这个很好理解。一般我们使用bind
的方式是fn.bind(obj)
。这里的this
就是fn
函数。上述demo
中的foo
。
if (this.prototype) { // Function.prototype doesn't have a prototype property fNOP.prototype = this.prototype; }
同理这里的this
也是指上述demo
中的foo
。所以foo.prototype
存在,设置fNOP.prototype = this.prototype;
。
接下来就是最关键的地方了:
var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function() {}, fBound = function() { return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); };
首先要明确的是bind
函数返回的就是fBound
函数,也就是外部的bar
函数。并且fToBind = this,
这里的this
指向的还是还是demo中的foo
函数。
最核心的这一行
this instanceof fNOP ? this : oThis, // 注3
的作用是区分fBound
函数的调用方式。
如果是通过上述demo中的bar('123');
这种方式调用,那么注3中的this就是window,this instanceof fNOP ? this : oThis
返回oThis,也就是demo中的obj对象。那么就相当于执行foo.apply(obj, 123),其结果就是改变了obj.name的值。
如果是通过var baz = new bar(‘abc’)这种方式调用。我们知道,使用new一个构造函数的过程中,会返回一个对象。并且构造函数中的this(在这里也就是foo中的this)会和该返回的对象baz绑定。所以this instanceof fNOP ? this : oThis
中的this就是该对象baz。由于fBound.prototype = new fNOP();
,所以fBound.prototype.__proto__指向FNOP.prototype。并且上面提到,bar函数就是fBound函数。所以baz是bar的一个实例,也就是fBound的一个实例。所以会有baz.__proto__指向fBound.prototype。并且fBound.prototype.__proto__指向FNOP.prototype,后者继续指向foo.prototype…所以baz的原型链上层是存在FNOP.prototype的。换言之,this instanceof fNOP ? this : oThis
就返回this,也就是baz这个新new出来的对象。所以bar.name的结果是abc。
总结
主要讲了bind函数的基本用法,this的多种绑定方式。并且polyfill中大量使用了闭包和原型链的技巧,都是js中的基础。想想看,我们的js基础真的扎实吗!
- Function.prototype.bind及其polyfill分析
- Function.prototype.bind实现
- Function.prototype.bind
- Function.prototype.bind重写
- 理解Function.prototype.bind
- Function.prototype.bind() ,Function.prototype.call() and function.prototype.apple()
- ECMA262 Edition5. Function.prototype.bind
- js关于Function.prototype.bind
- JavaScript 中的 Function.prototype.bind
- 理解 JavaScript 中的 Function.prototype.bind
- 理解 JavaScript 中的 Function.prototype.bind
- JavaScript中的Function.prototype.bind()方法简介
- 理解javascript的 Function.prototype.bind
- 理解 JavaScript 中的 Function.prototype.bind
- 理解 JavaScript 中的 Function.prototype.bind
- 理解 JavaScript 中的 Function.prototype.bind
- 理解 JavaScript 中的 Function.prototype.bind
- ECMASCript5新特性之Function.prototype.bind
- JAVA初学输入输出
- poi3.16读取xlsx excel2007+的官方例子
- 目标检测4 Faster R-CNN
- 单例模式
- 关于自动化运维的一些思考
- Function.prototype.bind及其polyfill分析
- 【Android】在ecplise上配置ADT, SDK, NDK以及用C/C++代码生成其他平台.so库的简单流程(Ubuntu 14.04)
- 正则表达式取url查询参数字符串
- 关于datagrid 垂直滚动条设置
- C#读取excel文件,生成json
- abap中VIEW_MAINTENANCE_GIVEN_DATA的用法
- junit 单元测试报错:java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing
- 交叉表格sql语句
- 2017年9月 bug总结