JavaScript中的new,bind,call,apply的简易实现
Function
原型链中的apply
,call
和bind
方法是 JavaScript 中相当重要的概念,与this
关键字密切相关,相当一部分人对它们的理解还是比较浅显,所谓js基础扎实,绕不开这些基础常用的API,这次让我们来了解它们吧!
实现new运算符
原理
new 关键字会进行如下的操作:
- 创建一个空的简单JavaScript对象(即{});
- 链接该对象(设置该对象的
constructor
)到另一个对象 ; - 将步骤1新创建的对象作为
this
的上下文 ; - 如果该函数没有返回对象,则返回
this
。
实现代吗
function _new(fn, ...args) {
if (typeof fn !== "function") {
throw TypeError("fn is not");
}
const obj = Object.create(fn.prototype);
const res = fn.call(obj, ...args);
const isObject = typeof res === "object" && res !== null;
return isObject ? res : obj;
}
实现bind方法
语法
function.bind(thisArg[, arg1[, arg2[, ...]]])
参数
thisArg
调用绑定函数时作为 this
参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用 bind
在 setTimeout
中创建一个函数(作为回调提供)时,作为 thisArg
传递的任何原始值都将转换为 object
。如果 bind
函数的参数列表为空,或者thisArg
是null
或undefined
,执行作用域的 this
将被视为新函数的 thisArg
。
arg1, arg2, ...
当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
返回值
返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。
原理
bind()
函数会创建一个新的绑定函数(bound function,BF)。绑定函数是一个 exotic function object(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数。
绑定函数具有以下内部属性:
[[BoundTargetFunction]]
- 包装的函数对象[[BoundThis]]
- 在调用包装函数时始终作为this
值传递的值。[[BoundArguments]]
- 列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。[[Call]]
- 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个this值和一个包含通过调用表达式传递给函数的参数的列表。
当调用绑定函数时,它调用 [[BoundTargetFunction]]
上的内部方法 [[Call]]
,就像这样 Call(boundThis, args)。其中,boundThis 是 [[BoundThis]]
,args 是 [[BoundArguments]]
加上通过函数调用传入的参数列表。
代码
有两种实现bind的方法,下面第一种不支持使用new调用新创建的构造函数,而第二种支持。
方法一
Function.prototype.myBind = function () {
const thatFunc = this,
thatArg = arguments[0],
slice = Array.prototype.slice;
let args = slice.call(arguments, 1);
if (typeof thatFunc !== "function") {
throw new TypeError(
"Function.prototype.bind - what is trying to be bound is not callable"
);
}
return function () {
var funcArgs = args.concat(slice.call(arguments));
return thatFunc.apply(thatArg, funcArgs);
};
};
方法二
Function.prototype.myBind = function (otherThis) {
if (typeof this !== "function") {
throw new TypeError(
"Function.prototype.bind - what is trying to be bound is not callable"
);
}
const ArrayPrototypeSlice = Array.prototype.slice;
let baseArgs = ArrayPrototypeSlice.call(arguments, 1),
baseArgsLength = baseArgs.length,
fToBind = this,
fNOP = function () {},
fBound = function () {
baseArgs.length = baseArgsLength; // reset to default base arguments
baseArgs.push.apply(baseArgs, arguments);
return fToBind.apply(
fNOP.prototype.isPrototypeOf(this) ? this : otherThis,
baseArgs
);
};
if (this.prototype) {
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};
实现call方法
语法
func.call([thisArg[, arg1, arg2, ...argN]])
参数
thisArg
可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
arg1, arg2, ...
指定的参数列表。
返回值
使用调用者提供的 this
值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined
。
实现代码
Function.prototype.myCall = function (ctx, ...arg) {
if (ctx === null || ctx === undefined) {
ctx = window
} else if (typeof ctx !== 'object' || typeof ctx !== 'function') {
ctx = Object(ctx)
}
const tmp = Symbol('tmp');
ctx[tmp] = this;
const result = ctx[tmp](...arg);
delete ctx[tmp];
return result;
};
apply方法的实现
注意:
apply()
方法的作用和call()
方法类似,区别就是call()
方法接受的是参数列表,而apply()
方法接受的是一个参数数组。
Function.prototype.myApply = function (ctx, arg) {
if (ctx === null || ctx === undefined) {
ctx = window
} else if (typeof ctx !== 'object' || typeof ctx !== 'function') {
ctx = Object(ctx)
}
const tmp = Symbol('tmp');
ctx[tmp] = this;
const result = ctx[tmp](...arg);
delete ctx[tmp];
return result;
};
参考书籍
- MDN-new运算符
- MDN-Function.prototype.bind()
- MDN-Function.prototype.call()
- MDN-Function.prototype.apply()
JavaScript中的new,bind,call,apply的简易实现的更多相关文章
- 理解JavaScript中的arguments,callee,caller,apply
arguments 该对象代表正在执行的函数和调用它的函数的参数. [function.]arguments[n] 参数function :选项.当前正在执行的 Function 对象的名字. n : ...
- [转] 理解 JavaScript 中的 Array.prototype.slice.apply(arguments)
假如你是一个 JavaScript 开发者,你可能见到过 Array.prototype.slice.apply(arguments) 这样的用法,然后你会问,这么写是什么意思呢? 这个语法其实不难理 ...
- JavaScript中callee与caller,apply与call解析
1. arguments.callee 1.1 解释 返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文. 1,.2 说明 callee 属性的初始值就是正被执行的 ...
- javascript中函数的call,apply及bind方法
call 方法调用一个对象的一个方法,以另一个对象替换当前对象.call([thisObj[,arg1[, arg2[, [,.argN]]]]])参数thisObj可选项.将被用作当前对象的对象. ...
- javascript中this、new、apply和call详解
如果在javascript语言里没有通过new(包括对象字面量定义).call和apply改变函数的this指针,函数的this指针都是指向window的,重要的话要说三遍.... 讲解this指针的 ...
- javascript 中的 arguments,callee.caller,apply,call 区别
记录一下: 1.arguments是一个对象, 是函数的一个特性,只有在函数内才具有这个特性,在函数外部不用使用. 举例: function test(){ alert(typeof argume ...
- Javascript中call函数和apply函数的使用
Javascript 中call函数和apply的使用: Javascript中的call函数和apply函数是对执行上下文进行切换,是将一个函数从当前执行的上下文切换到另一个对象中执行,例如: so ...
- 深入浅出 妙用Javascript中apply、call、bind
apply.call 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向. Jav ...
- 【优雅代码】深入浅出 妙用Javascript中apply、call、bind
这篇文章实在是很难下笔,因为网上相关文章不胜枚举. 巧合的是前些天看到阮老师的一篇文章的一句话: “对我来说,博客首先是一种知识管理工具,其次才是传播工具.我的技术文章,主要用来整理我还不懂的知识.我 ...
- Javascript中call,apply,bind方法的详解与总结
在 javascript之 this 关键字详解 文章中,谈及了如下内容,做一个简单的回顾: 1.this对象的涵义就是指向当前对象中的属性和方法. 2.this指向的可变性.当在全局作用域时,thi ...
随机推荐
- django学习-4.url动态传值
1.前言 我们在浏览器访问一个网页A是通过一个指定的url地址去访问的.但在浏览器用一个不存在的url地址去执行访问是打不开正确的网页的,只会打开一个浏览器自带的有错误提示的网页. 在django框架 ...
- Java基本概念:封装
一.简介 描述: 生活中,我们要看电视,只需要按一下开关和换台就可以了.我们没有有必要了解电视机内部的结构. 制造厂家为了方便我们使用电视,把复杂的内部细节全部封装起来,只给我们暴露简单的接口,比如电 ...
- rar密码破解工具汇总
rar密码破解工具汇总 前言 假如酷爱在网络上找各种资源的你,经历千辛万苦终于找到了一个rar打包的文件,兴奋地慌忙点击,可打开才发现是加密的,相信这样的场景很多人都遇到过,今天就针对压缩文件密码的破 ...
- 正则表达式匹配${key}并在Java中使用
1.正则表达式匹配${key} \$\{([a-z]+)\} 能够匹配字符串中以${key}形式的文本(其中key为小写应为字母) .*\$\{([a-z]+)\}.* 可以用来检测文本中是否有${k ...
- 纯生js实现Element中input组件的部分功能(慢慢完善)并封装成组件
现在实现的有基础用法.可清空.密码框,参考链接:https://element.eleme.cn/#/zh-CN/component/input HTML代码:想要测试哪个组件,直接将对应组件解开注释 ...
- iOS 14.5 有啥新功能?Apple Watch 也能解锁 iPhone 了
转: iOS 14.5 有啥新功能?Apple Watch 也能解锁 iPhone 了 苹果今天发布了即将发布的 iOS 14.5 和 iPadOS 14.5 更新的第一个 Beta 版本,我们在其中 ...
- 微信小程序日期转时间戳
let date = '2019-10-14'; var repTime = date.replace(/-/g, '/'); var timeTamp = Date.parse(repTime) / ...
- createNewFile() 报错 open failed: ENOENT (No such file or directory) 的解决方案
在写Android应用中使用createNewFile() 遇到open failed: ENOENT (No such file or directory) 错误,在网上查了许多方法,不过都不能解决 ...
- Java I/O流 05
I/O流·文件递归 统计该文件夹的大小 * 需求:从键盘就收一个文件夹路径,统计该文件夹的大小 package com.heima.test; import java.io.File; import ...
- linux时间问题
如果遇到创建时间和更新时间,不一致,先将时间调整为一致. 导致不一致的原因可能是时区不对,使用 tzselect ,将时区调整为Asia/Shanghai , cp /usr/share/zonein ...