apply, bind, call--绑定this的方法
Function.prototype.call(),Function.prototype.apply(),Function.prototype.bind()
是三种改变函数内部this指向(即函数执行时所在作用域)的方法。
1.Function.prototype.call(thisValue, param1, param2,....)
// 模拟源码
/**
* 1. 让函数立即执行
* 2. 改变函数内部的this指向
*/
Function.prototype.call = function call(context) {
if (this.name === 'call' && typeof context !== 'function') {
// 多个call函数连续调用
throw new TypeError('the first param should be a function');
}
context = context ? Object(context) : window;
context.fn = this; // 改变函数内部的this指向
const args = [];
for(let i=1; i<arguments.length; i++) {
args.push('arguments[' + i + ']');
}
// 立即执行;利用了数组的toString()属性
const r = eval('context.fn('+ args + ')');
delete context.fn;
return r;
}
1)第一个参数
第一个参数必须是对象,
- 如果是 空,undefine, null 都默认绑定到window;
- 如果是基础类型(如字符型,数值型,布尔型)会自动将基础类型转为包装类型,如: Number(5)
- 如果是this, 表示绑定的是当前作用域
var n = 123;
var obj = {
n: 456
}
function a() {
return this;
}
// this === window
a.call(); // window
a.call(null); // window
a.call(undefine); //window
a.call(window); // window
// this === obj
a.call(obj); // obj
// this === 包装对象
a.call(5); // Number(5) {[[PrimitiveValue]]: 5}
2) 其余的参数
其余的参数作为函数调用时传入的参数
function add(a,b) {
return a + b;
}
//this表示固定当前this的指向; this === window
add.call(this, 1, 2); // 3
3)应用
// 调用对象的原生方法
var obj = {
a: 5
}
console.log(obj.hasOwnProperty('toString')); // fasle
obj.hasOwnProperty = function() { // 覆盖obj对象继承自原型链上的方法
return true; // 改变的是obj本身的方法,原型链上的方法不变
}
console.log(obj.hasOwnProperty('toString')); // true
// obj可以使用原型链上的方法,表示在obj的作用域使用hasOwnProperty的方法
console.log(Object.prototype.hasOwnProperty.call(obj)); // false
2. Function.prototype.apply(thisValue, [param1, ....])
// 模拟源码
/**
* 1. apply方法让函数立即执行
* 2. apply方法绑定函数内部的this指向
* 3. 参数以数组形式传递
*/
Function.prototype.apply = function apply(context, args) {
if (this.name === 'apply' && typeof context !== 'function') {
// 多个apply函数连续调用;apply作为函数原型链上的方法,只能被函数调用
throw new TypeError('the first param should be a function');
}
context = context ? Object(context) : window;
context.fn = this;
if(!args) {// 如果不传参
return context.fn();
}
const r = eval('context.fn('+ args +')');
delete context.fn;
return r;
}
1)第一个参数和call方法规则相同
2)第二个参数
第二个参数是数组, 将数组中的参数依次作为调用函数的参数,如果函数中参数个数少于apply传参个数,只取前面的。
function add(a,b) {
console.log(a,b); // 1 2
return a + b;
}
add.apply(null, [1, 2, 3, 4, 5]);
第二个参数还可以是类数组,如arguments
function add(a,b) {
console.log(a,b); // 3,4
return a + b;
}
function newAdd() {
return add.apply(null, arguments);
}
var result = newAdd(3,4,5,5);
console.log(result); //
3)应用
//1) 查找数组的最大值
const arr = [1,3,5];
Math.max.apply(null, arr); //
// 还可以
Math.max(...arr); // // 2) 将数组的空项转为undefined;undefine可以被遍历,空会被遍历函数忽略
const arr = [1,,4];
Array.apply(null, arr); // [1,undefined,4] Array是数组的构造函数
3. Function.prototype.bind(thisValue, params, param2...)
// 模拟源码
/**
* 1. bind可以绑定this的指向;bind还可以绑定参数;
* 最后的参数=bind时传入的参数+新函数的参数
* 2. bind绑定后返回一个新的函数
* 3. 如果返回的函数被使用了new命令,this指向实例对象
* 4. new命令生成的实例可以找到原有类的原型对象
*/
Function.prototype.bind = function bind(context) {
context = context ? Object(context) : window;
// 获取bind时传入的参数
const boundArgs = Array.prototype.slice.call(arguments, 1);
const that = this; //原函数
let boundFn = function() {
const args = Array.prototype.slice.call(arguments, 1);
if (this instanceof boundFn) {// 说明使用了new命令
context = this; //new绑定 > 显示绑定
}
return that.apply(context, boundArgs.concat(args));
};
// 新生成的函数继承bind之前的原型对象
function Fn() {}
Fn.prototype = this.prototype;
boundFn.prototype = new Fn();
// 返回新函数
return boundFn;
}
1)第一个参数
同call,apply方法
2)剩余的参数
当bind(thisValue, ...)后面参数的个数小于原函数的个数时,绑定部分参数;
// 绑定部分参数;相当于参数复用,只需要处理剩余的参数
function fn(a,b) {
return a + b
}
var newFn = fn.bind(null, 5)
/*
newFn = function(b) {
return 5 + b
}
*/
console.log(newFn(6)); //
3)应用
- bind方法每次运行都返回一个新的函数;在监听事件时需要注意
document.addEventListener('click', obj.fn.bind(this))
// 下面取消绑定无效;因为是不同的函数
document.removeEventListener('click',obj.fn.bind(this))
正确的写法写法应该是
var listener = obj.fn.bind(this);
document.addEventListener('click', listener);
document.removeEventListener('click', listener);
- 结合回调函数使用
obj.print = function () {
this.times.forEach(function (n) {
console.log(this.name);
}.bind(this));
}; obj.print()
// 张三
// 张三
// 张三
和call方法结合使用;改变传参格式
[1, 2, 3].slice(0, 1) // [1]
// 等同于
Array.prototype.slice.call([1, 2, 3], 0, 1) // [1]
// 上面代码的意思是在Array.prototype.slice对象上调用call方法
// 即
var mySlice = Function.prototype.call.bind(Array.prototype.slice);
console.log(mySlice([1,2,3], 0, 1)) // 能调用slice的对象是第一个参数
//同理
var myPush = Function.prototype.call.bind(Array.prototype.push);
// bind方法传参也可以被改变
function f() {
console.log(this.a);
}
var obj = {a: 1}
var myBind = Function.prototype.call.bind(Function.prototype.bind); // 能调用bind方法只能是function
myBind(f, obj)(); //
4. apply.call,bind之间的区别
1)call和apply方法调用后,相当于绑定this后立即执行
2)bind方法是返回一个新的函数,并不立即执行
应用:
1)将类数组转为数组
// 1)将类数组转为数组
// apply,call方法立即执行
Array.prototype.slice.apply({ 0: 1, length: 1,});
Array.prototype.slice.call({ 0: 1, length: 1,});
// bind方法生成一个新的函数,需要手动执行,后面加()
Array.prototype.slice.bind({ 0: 1, length: 1,})();
2)给回调函数绑定对象
// 2)绑定回调函数的对象;
// 未进行绑定前,回调函数中的this一般都是window
var name = 'Hello World';
var obj = {
name: 'Lyra',
times: [1,2,4],
print: function() {
console.log(this === obj); // true
this.times.forEach(function() {
console.log(this === window); // true
console.log(this.name); // Hello World
})
}
}
obj.print(); // 使用bind方法绑定回调函数中的this
var name = 'Hello World';
var obj = {
name: 'Lyra',
times: [1,2,4],
print: function() {
this.times.forEach((function() {
console.log(this); // obj --3次
console.log(this.name); // Lyra --3次
}.bind(this))) // 不能用call,apply替换,因为会立即执行,就不再是函数了,会返回函数的默认返回值undefined
}
}
obj.print(); // 使用call, apply方法绑定回调函数中的this;
var name = 'Hello World';
var obj = {
name: 'Lyra',
times: [1,2,4],
print: function() {
const that = this;
this.times.forEach((function() {// 因为apply,call会立即执行,所以要嵌套一层函数
(function IIFE() {
console.log(this); // obj --3次
console.log(this.name); // Lyra --3次
}).call(that); // 可以替换成apply。IIFE需要用括号扩起来变为函数表达式。否则函数声明不能调用call方法。
}))
}
}
obj.print();
apply, bind, call--绑定this的方法的更多相关文章
- 别真以为JavaScript中func.call/apply/bind是万能的!
自从学会call/apply/bind这三个方法后我就各种场合各种使用各种得心应手至今还没踩过什么坑,怎么用?说直白点就是我自己的对象没有某个方法但别人有,我就可以通过call/apply/bind去 ...
- JS 的 call apply bind 方法
js的call apply bind 方法都很常见,目的都是为了改变某个方法的执行环境(context) call call([thisObj[,arg1[, arg2[, [,.argN]]]] ...
- call, apply,bind 方法解析
call(), apply(),bind() 三者皆为Function的方法 call(),apply()的作用是调用方法,并改变函数运行时的context(作用上下文) bind() 的作用是引用方 ...
- call,apply,bind 方法的学习
这是三个常用的操作函数的方法,在js中函数就是一等公民,所以说掌握这三个方法还是有必要的 call 和 apply,都会绑定函数的上下文(context)并立即执行调用该方法函数,两者区别在于,接受的 ...
- JavaScript内置一些方法的实现原理--new关键字,call/apply/bind方法--前戏
new关键字,call/apply/bind方法都和this的绑定有关,在学习之前,首先要理解this. 一起来学习一下this吧 首先.this是一个对象. 对象很好理解,引用类型值,可以实现如th ...
- JavaScript中call,apply,bind方法的总结。
why?call,apply,bind干什么的?为什么要学这个? 一般用来指定this的环境,在没有学之前,通常会有这些问题. var a = { user:"追梦子", fn:f ...
- call,apply,bind方法的总结
why?call,apply,bind干什么的?为什么要学这个? 一般用来指定this的环境,在没有学之前,通常会有这些问题. var a = { user:"追梦子", fn:f ...
- JavaScript中call,apply,bind方法的总结
原文链接:http://www.cnblogs.com/pssp/p/5215621.html why?call,apply,bind干什么的?为什么要学这个? 一般用来指定this的环境,在没有学之 ...
- JS中call,apply,bind方法的总结
why?call,apply,bind干什么的?为什么要学这个? 一般用来指定this的环境,在没有学之前,通常会有这些问题. var a = { user: "小马扎", fn: ...
随机推荐
- HTML基础之三(form表单)
.表单form 单是一个包含表单元素的区域. 表单能够包含 input 元素,textarea.select.fieldset.legend 和 label 元素. 表单使用标签(<form&g ...
- Python list,tuple,dict,set高级变量常用方法
list列表 增加 append 在列表中追加,一次只能加一个 insert 按索引插入,一次只能插一个 extend 迭代追加到列表中 list1 = [1, 2, 3] list2 = [4, 5 ...
- 微信小程序 路由跳转 异步请求 存储数据,微信登录接口
1小程序路由跳转 wx.switchTab(Object object) 这里的tabBar是底下的导航栏指定的页面 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面 tabBar l ...
- python学习-7 条件语句 while循环 + 练习题
1.死循环 while 1 == 1: print('ok') 结果是一直循环 2.循环 count = 0 while count < 10: print(count) count = cou ...
- Prometheus Operator 的安装
Prometheus Operator 的安装 接下来我们用自定义的方式来对 Kubernetes 集群进行监控,但是还是有一些缺陷,比如 Prometheus.AlertManager 这些组件服务 ...
- Oracle练习(一)
Oracle 1.检索部门编号.部门名称.部门所在地及其每个部门的员工总数. select d.deptno,d.dname,d.loc,count(*) from emp e,dept d wher ...
- codeforce E - Minimal Labels+hdu 4857
两个题目的意思差不多 都是希望得出的拓扑序如果有多种 要求输出字典序小的情况 这里引用一个大佬的博客 关于为什么不能直接建图然后用小根堆解决这个问题(http://blog.csdn.net/rgno ...
- 基于MFC对话框的2048游戏
在之前一篇<简单数字拼板游戏学习>基础上修改,地址:http://www.cnblogs.com/fwst/p/3706483.html 开发环境:Windows 7/ Visual St ...
- STM32-移植FATFS的NANDFLASH驱动
一,建立工程FATFS源码 1,在http://elm-chan.org/fsw/ff/00index_e.html上下载ff007c.zip,并把ff007c.zip里面的 src文件夹复制到D:\ ...
- zepto学习(二)之tap事件以及tap事件点透处理
前言 为什么通过touch可以触发click事件? touch事件的来源 PC网页上的大部分操作都是用鼠标的,即响应的是鼠标事件,包括mousedown.mouseup.mousemove和click ...