bind
官方描述
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
使用介绍
由于javascript中作用域是由其运行时候所处的环境决定的,所以往往函数定义和实际运行的时候所处环境不一样,那么作用域也会发生相应的变化。
例如下面这个情况:
var id = 'window'; //定义一个函数,但是不立即执行 var test = function(){ console.log(this.id) } test() // window //把test作为参数传递 var obj = { id:'obj', hehe:test } //此时test函数运行环境发生了改变 obj.hehe() // 'obj' //为了避免这种情况,javascript里面有一个bind方法可以在函数运行之前就绑定其作用域,修改如下 var id = 'window'; var test = function(){ console.log(this.id) }.bind(window) var obj = { id:'obj', hehe:test } test() // window obj.hehe() // window
上面介绍了bind方法的一个重要作用就是为一个函数绑定作用域,但是bind方法在低版本浏览器不兼容,这里我们可以手动实现一下。
拆分一下关键思路
因为bind方法不会立即执行函数,需要返回一个待执行的函数(这里用到闭包,可以返回一个函数)return function(){}
作用域绑定,这里可以使用apply或者call方法来实现 xx.call(yy)/xx.apply(yy)
参数传递,由于参数的不确定性,需要用apply传递数组(实例更明了)xx.apply(yy,[...Array...]),如果用call就不太方便了,因为call后面的参数需要一个个列出来
实现
有了上述的思路,大致的雏形已经明了了,代码应该也很容易实现
绑定作用域,绑定传参
Function.prototype.testBind = function(that){ var _this = this, /* *由于参数的不确定性,统一用arguments来处理,这里的arguments只是一个类数组对象,有length属性 *可以用数组的slice方法转化成标准格式数组,除了作用域对象that以外, *后面的所有参数都需要作为数组参数传递 *Array.prototype.slice.apply(arguments,[1])/Array.prototype.slice.call(arguments,1) */ slice = Array.prototype.slice, args = slice.apply(arguments,[1]); //返回函数 return function(){ //apply绑定作用域,进行参数传递 return _this.apply(that,args) } }
测试
var test = function(a,b){ console.log('作用域绑定 '+ this.value) console.log('testBind参数传递 '+ a.value2) console.log('调用参数传递 ' + b) } var obj = { value:'ok' } var fun_new = test.testBind(obj,{value2:'also ok'}) fun_new ('hello bind') // 作用域绑定 ok // testBind参数传递 also ok // 调用参数传递 undefined
动态参数
上面已经实现了bind方法的作用域绑定,但是美中不足的是,既然我们返回的是一个函数,调用的时候应该支持传递参数,很显然,上面的 fun_new 调用的时候并不支持传参,只能在 testBind 绑定的时候传递参数,因为我们最终调用的是这个返回函数
function(){ return _this.apply(that,args) } 这里面的args在绑定的时候就已经确定了,调用的时候值已经固定, 我们并没有处理这个function传递的参数。
我们对其进行改造
return function(){ return _this.apply(that, args.concat(Array.prototype.slice.apply(arguments,[0])) ) }
这里的 Array.prototype.slice.apply(arguments,[0]) 指的是这个返回函数执行的时候传递的一系列参数,所以是从第一个参数开始 [0] ,之前的args = slice.apply(arguments,[1])指的是 testBind方法执行时候传递的参数,所以从第二个开始 [1],两则有本质区别,不能搞混,只有两者合并了之后才是返回函数的完整参数
所以有如下实现
Function.prototype.testBind = function(that){ var _this = this, slice = Array.prototype.slice, args = slice.apply(arguments,[1]); return function(){ return _this.apply(that, args.concat(Array.prototype.slice.apply(arguments,[0])) ) } }
测试
var test = function(a,b){ console.log('作用域绑定 '+ this.value) console.log('testBind参数传递 '+ a.value2) console.log('调用参数传递 ' + b) } var obj = { value:'ok' } var fun_new = test.testBind(obj,{value2:'also ok'}) fun_new ('hello bind') // 作用域绑定 ok // testBind参数传递 also ok // 调用参数传递 hello bind
在以上2种传参方式中,bind的优先级高,从 args.concat(Array.prototype.slice.apply(arguments,[0])) 也可以看出来,bind的参数在数组前面。
原型链
官方文档上有一句话:
A bound function may also be constructed using the new operator: doing
so acts as though the target function had instead been constructed.
The provided this value is ignored, while prepended arguments are
provided to the emulated function.
说明绑定过后的函数被new实例化之后,需要继承原函数的原型链方法,且绑定过程中提供的this被忽略(继承原函数的this对象),但是参数还是会使用。
这里就需要一个中转函数把原型链传递下去
fNOP = function () {} //创建一个中转函数 fNOP.prototype = this.prototype; xx.prototype = new fNOP() 修改如下 Function.prototype.testBind = function(that){ var _this = this, slice = Array.prototype.slice, args = slice.apply(arguments,[1]), fNOP = function () {}, //所以调用官方bind方法之后 有一个name属性值为 'bound ' bound = function(){ return _this.apply(that, args.concat(Array.prototype.slice.apply(arguments,[0])) ) } fNOP.prototype = _this.prototype; bound.prototype = new fNOP(); return bound; }
而且bind方法的第一个参数this是可以不传的,需要分2种情况
直接调用bind之后的方法
var f = function () { console.log('不传默认为'+this) };f.bind()() // 不传默认为 Window
javascript原生一步步实现bind分析
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。
作者:谁谁来源:segmentfault|2016-11-02 18:54
收藏
分享
bind
官方描述
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
使用介绍
由于javascript中作用域是由其运行时候所处的环境决定的,所以往往函数定义和实际运行的时候所处环境不一样,那么作用域也会发生相应的变化。
例如下面这个情况:
var id = 'window'; //定义一个函数,但是不立即执行 var test = function(){ console.log(this.id) } test() // window //把test作为参数传递 var obj = { id:'obj', hehe:test } //此时test函数运行环境发生了改变 obj.hehe() // 'obj' //为了避免这种情况,javascript里面有一个bind方法可以在函数运行之前就绑定其作用域,修改如下 var id = 'window'; var test = function(){ console.log(this.id) }.bind(window) var obj = { id:'obj', hehe:test } test() // window obj.hehe() // window
上面介绍了bind方法的一个重要作用就是为一个函数绑定作用域,但是bind方法在低版本浏览器不兼容,这里我们可以手动实现一下。
拆分一下关键思路
因为bind方法不会立即执行函数,需要返回一个待执行的函数(这里用到闭包,可以返回一个函数)return function(){}
作用域绑定,这里可以使用apply或者call方法来实现 xx.call(yy)/xx.apply(yy)
参数传递,由于参数的不确定性,需要用apply传递数组(实例更明了)xx.apply(yy,[...Array...]),如果用call就不太方便了,因为call后面的参数需要一个个列出来
实现
有了上述的思路,大致的雏形已经明了了,代码应该也很容易实现
绑定作用域,绑定传参
Function. prototype. testBind = function(that){ var _this = this, /* *由于参数的不确定性,统一用arguments来处理,这里的arguments只是一个类数组对象,有length属性 *可以用数组的slice方法转化成标准格式数组,除了作用域对象that以外, *后面的所有参数都需要作为数组参数传递 *Array.prototype.slice.apply(arguments,[1])/Array.prototype.slice.call(arguments,1) */ slice = Array.prototype.slice, args = slice.apply(arguments,[1]); //返回函数 return function(){ //apply绑定作用域,进行参数传递 return _this.apply(that,args) } }
测试
var test = function(a,b){ console.log('作用域绑定 '+ this.value) console.log('testBind参数传递 '+ a.value2) console.log('调用参数传递 ' + b) } var obj = { value:'ok' } var fun_new = test.testBind(obj,{value2:'also ok'}) fun_new ('hello bind') // 作用域绑定 ok // testBind参数传递 also ok // 调用参数传递 undefined
动态参数
上面已经实现了bind方法的作用域绑定,但是美中不足的是,既然我们返回的是一个函数,调用的时候应该支持传递参数,很显然,上面的 fun_new 调用的时候并不支持传参,只能在 testBind 绑定的时候传递参数,因为我们最终调用的是这个返回函数
function(){ return _this.apply(that,args) } 这里面的args在绑定的时候就已经确定了,调用的时候值已经固定, 我们并没有处理这个function传递的参数。
我们对其进行改造
return function(){ return _this.apply(that, args.concat(Array.prototype.slice.apply(arguments,[0])) ) }
这里的 Array.prototype.slice.apply(arguments,[0]) 指的是这个返回函数执行的时候传递的一系列参数,所以是从第一个参数开始 [0] ,之前的args = slice.apply(arguments,[1])指的是 testBind方法执行时候传递的参数,所以从第二个开始 [1],两则有本质区别,不能搞混,只有两者合并了之后才是返回函数的完整参数
所以有如下实现
Function. prototype.testBind = function(that){ var _this = this, slice = Array.prototype.slice, args = slice.apply(arguments,[1]); return function(){ return _this.apply(that, args.concat(Array.prototype.slice.apply(arguments,[0])) ) } }
测试
var test = function(a,b){ console.log('作用域绑定 '+ this.value) console.log('testBind参数传递 '+ a.value2) console.log('调用参数传递 ' + b) } var obj = { value:'ok' } var fun_new = test.testBind(obj,{value2:'also ok'}) fun_new ('hello bind') // 作用域绑定 ok // testBind参数传递 also ok // 调用参数传递 hello bind
在以上2种传参方式中,bind的优先级高,从 args.concat(Array.prototype.slice.apply(arguments,[0])) 也可以看出来,bind的参数在数组前面。
原型链
官方文档上有一句话:
A bound function may also be constructed using the new operator: doing
so acts as though the target function had instead been constructed.
The provided this value is ignored, while prepended arguments are
provided to the emulated function.
说明绑定过后的函数被new实例化之后,需要继承原函数的原型链方法,且绑定过程中提供的this被忽略(继承原函数的this对象),但是参数还是会使用。
这里就需要一个中转函数把原型链传递下去
fNOP = function () {} //创建一个中转函数 fNOP.prototype = this.prototype; xx.prototype = new fNOP() 修改如下 Function.prototype.testBind = function(that){ var _this = this, slice = Array.prototype.slice, args = slice.apply(arguments,[1]), fNOP = function () {}, //所以调用官方bind方法之后 有一个name属性值为 'bound ' bound = function(){ return _this.apply(that, args.concat(Array.prototype.slice.apply(arguments,[0])) ) } fNOP.prototype = _this.prototype; bound.prototype = new fNOP(); return bound; }
而且bind方法的第一个参数this是可以不传的,需要分2种情况
直接调用bind之后的方法
var f = function () { console.log('不传默认为'+this) };f.bind()() // 不传默认为 Window
所以直接调用绑定方法时候 apply(that, 建议改为 apply(that||window,,其实不改也可以,因为不传默认指向window
使用new实例化被绑定的方法
容易糊涂,重点在于弄清楚标准的bind方法在new的时候做的事情,然后就可以清晰的实现
这里我们需要看看 new 这个方法做了哪些操作 比如说 var a = new b()
创建一个空对象 a = {},并且this变量引用指向到这个空对象a
继承被实例化函数的原型 :a.__proto__ = b.prototype
被实例化方法b的this对象的属性和方法将被加入到这个新的 this 引用的对象中: b的属性和方法被加入的 a里面
新创建的对象由 this 所引用 :b.call(a)
通过以上可以得知,如果是var after_new = new bindFun(); 由于这种行为是把原函数当成构造器,那么那么最终实例化之后的对象this需要继承自原函数, 而这里的 bindFun 目前是
function(){ return _this.apply(that || window, args.concat(Array.prototype.slice.apply(arguments,[0])) ) }
这里apply的作用域是绑定的that || window,在执行 testBind()的时候就已经固定,并没有把原函数的this对象继承过来,不符合我们的要求,我们需要根据apply的特性解决这个问题:
在一个子构造函数中,你可以通过调用父构造函数的 `apply/call` 方法来实现继承
例如
function Product(name, price) { this.name = name; this.price = price; if (price < 0) { throw RangeError('Cannot create product ' + this.name + ' with a negative price'); } } function Food(name, price) { Product.call(this, name, price); this.category = 'food'; } //等同于(其实就是把Product放在Food内部执行了一次) function Food(name, price) { this.name = name; this.price = price; if (price < 0) { throw RangeError('Cannot create product ' + this.name + ' with a negative price'); } this.category = 'food'; }
所以在new新的实例的时候实时将这个新的this对象 进行 apply 继承原函数的 this 对象,就可以达到 new 方法里面的第 3 步的结果
apply(that||window, //修改为 如果是new的情况,需要绑定new之后的作用域,this指向新的实例对象 apply(isNew ? this : that||window, ==> Function.prototype.testBind = function(that){ var _this = this, slice = Array.prototype.slice, args = slice.apply(arguments,[1]), fNOP = function () {}, //所以调用官方bind方法之后 有一个name属性值为 'bound ' bound = function(){ return _this.apply(isNew ? this : that||window, args.concat(Array.prototype.slice.apply(arguments,[0])) ) } fNOP.prototype = _this.prototype; bound.prototype = new fNOP(); return bound; }
这里的 isNew 是区分 bindFun 是直接调用还是被 new 之后再调用,通过原型链的继承关系可以知道,
bindFun 属于 after_new的父类,所以 after_new instanceof bindFun 为 true,同时
bindFun.prototype = new fNOP() 原型继承; 所以 fNOP 也是 after_new的父类, after_new instanceof fNOP 为 true
最终结果
Function.prototype.testBind = function(that){ var _this = this, slice = Array.prototype.slice, args = slice.apply(arguments,[1]), fNOP = function () {}, bound = function(){ //这里的this指的是调用时候的环境 return _this.apply(this instanceof fNOP ? this : that||window, args.concat(Array.prototype.slice.apply(arguments,[0])) ) } fNOP.prototype = _this.prototype; bound.prototype = new fNOP(); return bound; }
我看到有些地方写的是
this instanceof fNOP && that ? this : that || window,
我个人觉得这里有点不正确,如果绑定时候不传参数,那么that就为空,那无论怎样就只能绑定 window作用域了。
以上就是javascript原生一步步实现bind分析的内容,更多相关内容请关注PHP中文网(www.php.cn)!

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于Symbol类型、隐藏属性及全局注册表的相关问题,包括了Symbol类型的描述、Symbol不会隐式转字符串等问题,下面一起来看一下,希望对大家有帮助。

怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯CSS也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助!

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于面向对象的相关问题,包括了属性描述符、数据描述符、存取描述符等等内容,下面一起来看一下,希望对大家有帮助。

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

foreach不是es6的方法。foreach是es3中一个遍历数组的方法,可以调用数组的每个元素,并将元素传给回调函数进行处理,语法“array.forEach(function(当前元素,索引,数组){...})”;该方法不处理空数组。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于BOM操作的相关问题,包括了window对象的常见事件、JavaScript执行机制等等相关内容,下面一起来看一下,希望对大家有帮助。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

MinGW - 适用于 Windows 的极简 GNU
这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

SublimeText3 英文版
推荐:为Win版本,支持代码提示!

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境