JavaScript中call,apply,bind的区别与实现
作者:XerDemo 发布时间:2024-04-22 12:51:26
区别
call、apply、bind相同点:都是改变this的指向,传入的第一个参数都是绑定this的指向,在非严格模式中,如果第一个参数是nul或者undefined,会把全局对象(浏览器是window)作为this的值,要注意的是,在严格模式中,null 就是 null,undefined 就是 undefined
call和apply唯一的区别是:call传入的是参数列表,apply传入的是数组,也可以是类数组
bind和call、apply的区别: bind返回的是一个改变了this指向的函数,便于稍后调用,不像call和apply会立即调用;bind和call很像,传入的也是参数列表,但是可以多次传入,不需要像call,一次传入
值得注意:当 bind 返回的函数 使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效 (new调用的优先级 > bind调用)
call实现
对象context想调用一个它没有的方法f 怎么办呢?f.call(context)
通过call来借用方法f ,怎么做到的呢?
对象context添加f方法
对象context执行f方法
Function.prototype.myCall = function(context, ...arg) {
// 如果第一个参数传入的是undefined和null,context为window对象
context = context || window;
// 为context对象添加函数bar
context.fn = this; // this:bar,this指向调用myCall的bar
// context对象执行函数bar,并返回结果
return context.fn(...arg);
}
// 测试一下
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.myCall(null); // 2
console.log(bar.myCall(obj, 'kevin', 18)); //1
// Object {
// value: 1,
// name: 'kevin',
// age: 18
// }
apply实现
apply和call唯一的区别是:call传入的是参数列表,apply传入的是数组,也可以是类数组
Function.prototype.myApply = function(context, arg) {
// 如果第一个参数传入的是undefined和null,context为window对象
context = context || window;
// context对象添加函数bar
context.fn = this; // this:bar,this指向调用myCall的函数bar
// context对象执行函数bar,并返回结果
let result = null;
if (!arg) { // 没有传入数组
result = context.fn();
}else{ // 传入了参数数组
result = context.fn(...arg);
}
return result;
}
// 测试一下
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.myApply(null); // 2
console.log(bar.myApply(obj, ['kevin', 18])); // 1
bind实现
bind和call、apply的区别: bind返回的是一个改变了this指向的函数,便于稍后调用,不像call和apply会立即调用;bind和call很像,传入的也是参数列表,但是可以多次传入,不需要像call,一次传入
值得注意:当 bind 返回的函数 使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效 (new调用的优先级 > bind调用)
bind实现最为复杂,因为经过bind绑定过的函数,既可以被当作普通函数调用,又可以被当作构造函数调用
bind 返回的函数 作为普通函数调用
// bind 返回的函数 作为普通函数调用
let bindFun = normalFun.myBind(obj, '我是参数传进来的name') // this:obj
bindFun('我是参数传进来的age')
bind 返回的函数 作为构造函数调用,绑定的 this 值obj会失效,this指向实例对象a
// bind 返回的函数 作为构造函数调用
let bindFun = Person.myBind(obj, '我是参数传进来的name') // this:obj
let a = new bindFun('我是参数传进来的age') // this:a
bind 返回的函数 作为普通函数调用 代码实现
// bind 返回的函数 作为普通函数调用
Function.prototype.myBind = function (context, ...args){
// 如果第一个参数传入的是undefined和null,context为window对象
context = context || window;
// context对象(obj)添加函数normalFun
context.fn = this; // this:normalFun, context.fn === normalFun,下面出现context.fn都可以直接看成normalFun
// bind返回的函数
return function (...innerArgs) {
// bind 返回的函数 作为普通函数被执行
context.fn(...[...args,...innerArgs]); //相当于normalFun(...[...args,...innerArgs])
}
}
// 测试
let obj = {
objName: '我是obj传进来的name',
objAge: '我是obj传进来的age'
}
// 普通函数
function normalFun(name, age) {
console.log(name); //'我是第一次参数传进来的name被args接收'
console.log(age); //'我是第二次参数传进来的age被innerArgs接收'
console.log(this === obj); // true,this指向obj
console.log(this.objName); //'我是obj传进来的name'
console.log(this.objAge); //'我是obj传进来的age'
}
// bind 返回的函数 作为普通函数调用
let bindFun = normalFun.myBind(obj, '我是第一次参数传进来的name被args接收'); // this指向obj
bindFun('我是第二次参数传进来的age被innerArgs接收');
bind 返回的函数 作为构造函数调用
// bind 返回的函数 再经过new调用
Function.prototype.myBind = function (context, ...args){
// 如果第一个参数传入的是undefined和null,context为window对象
context = context || window;
// context对象添加函数Person
context.fn = this; // this:Person,context.fn:Person,_this:Person
let _this = this;
// bind返回的函数
const result = function (...innerArgs) {
if (this instanceof _this ) { // this:a (new出来的实例对象) , _this:Person
// 为实例对象a添加Person方法
this.fn = _this;
// 实例对象a执行Person方法
this.fn(...[...args,...innerArgs]);
}
}
result.prototype = Object.create(this.prototype); // 为什加这一句?看原型图下面会解释
return result;
}
// 测试
function Person(name, age) {
console.log(name); //'我是第一次参数传进来的name被args接收'
console.log(age); //'我是第二次参数传进来的age被innerArgs接收'
console.log(this); //构造函数this指向实例对象
}
// 构造函数原型的方法
Person.prototype.say = function() {
console.log(123);
}
let obj = {
objName: '我是obj传进来的name',
objAge: '我是obj传进来的age'
}
// bind 返回的函数 作为构造函数调用
let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj
let a = new bindFun('我是第二次参数传进来的age被innerArgs接收') // this:a
a.say() //123
画以下两条语句的原型图方便理解
let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj
let a = new bindFun('我是第二次参数传进来的age被innerArgs接收') // this:a
当执行下面语句时,原型图如下:
let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj
当执行下面语句时,bindFun就是result看代码,原型图如下:
let a = new bindFun('我是第二次参数传进来的age被innerArgs接收') // this:a
在这里实例对象a还需要继承构造函数Person的原型,所以加上了
result.prototype = Object.create(this.prototype);
原型图最终如下:
bind代码最终实现
Function.prototype.myBind = function (context, ...args){
// 如果第一个参数传入的是undefined和null,context为window对象
context = context || window;
// context对象添加函数Person
context.fn = this; // this:Person,context.fn:Person,_this:Person
let _this = this;
// bind返回的函数
const result = function (...innerArgs) {
if (this instanceof _this ) { // this:a (new出来的实例对象) , _this:Person
// 为实例对象a添加Person方法
this.fn = _this;
// 实例对象a执行Person方法
this.fn(...[...args,...innerArgs]);
}else{
// 普通函数被调用
context.fn(...[...args,...innerArgs]);
}
}
result.prototype = Object.create(this.prototype); // 为什加这一句?看原型图下面会解释
return result;
}
// 测试
// function Person(name, age) {
// console.log(name); //'我是第一次参数传进来的name被args接收'
// console.log(age); //'我是第二次参数传进来的age被innerArgs接收'
// console.log(this); //构造函数this指向实例对象
// }
// // 构造函数原型的方法
// Person.prototype.say = function() {
// console.log(123);
// }
// let obj = {
// objName: '我是obj传进来的name',
// objAge: '我是obj传进来的age'
// }
// // bind 返回的函数 作为构造函数调用
// let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj
// let a = new bindFun('我是第二次参数传进来的age被innerArgs接收') // this:a
// a.say() //123
// 测试
let obj = {
objName: '我是obj传进来的name',
objAge: '我是obj传进来的age'
}
// 普通函数
function normalFun(name, age) {
console.log(name); //'我是第一次参数传进来的name'
console.log(age); //'我是第二次参数传进来的age'
console.log(this === obj); // true
console.log(this.objName); //'我是obj传进来的name'
console.log(this.objAge); //'我是obj传进来的age'
}
// bind 返回的函数 作为普通函数调用
let bindFun = normalFun.myBind(obj, '我是第一次参数传进来的name被args接收'); // this指向obj
bindFun('我是第二次参数传进来的age被innerArgs接收');
来源:https://juejin.cn/post/7142775964009758757
猜你喜欢
- 本文实例总结了Python列表list常用内建函数。分享给大家供大家参考,具体如下:>>> x = list(range(
- Dreamweaver一直是不少网友钟情的网页设计工具,除了它强大的动态效果制作能力外,方便简洁的操作界面更是独具特色,下面我们将以最新版本
- 前言1992年扫雷被加入到windows3.1,成为早期windows的经典游戏。近来接触python的GUI(图形化)编程,于是通过编写扫
- 一、常见的匹配规则二、常见的匹配方法1、match()match()方法从字符串的起始位置开始匹配,该方法有两个参数,第一个是正则表达式,第
- 本文实例讲述了PHP使用ffmpeg给视频增加字幕显示的方法。分享给大家供大家参考。具体实现方法如下:<?php$dir = '
- 一、选择文件夹首先导入PySimpleGUI库,并且用缩写sg来表示。import PySimpleGUI as sg# 窗口显示文本框和浏
- 这里就不给大家废话了,直接上代码,代码的解释都在注释里面,看不懂的也别来问我,好好学学基础知识去!# -*- coding: utf-8 -
- 1.安装1.1 创建虚拟环境mkdir myprojectcd myprojectpython3 -m venv venv1.2 进入虚拟环
- 小引 笔者认为web开发包括设计html,javascript,css,以及一种高级语言,比如c#,java等等,本文分为三部分,第一部分为
- pyecharts中的Funnel函数可以绘制漏斗图,自动根据数据大小生成由大到小自上而下排列的一个漏斗样的图形。1、导入Funnel模块。
- 前言pygame中的精灵碰撞是可见游戏中用的最基础的东西,这里结合官方文档和小甲鱼的网站上的内容做个小总结,方便日后使用。pygame.sp
- Vue在目前是很好的框架,第一次使用Vue开发项目,刚开始的时候在一个控制开关的组件都花费了很久的时间,问题解决了,把自己的一些小问题给记录
- 此功能已进入试用一周了,都没有问题,奇怪的是今天出现了问题?? AjaxRequestObj.createEqStatusParameter
- Python计算的位数在电脑上做了一个实验,看看python能计算到多少位,一下是结果。x = math.sqrt((3))print (&
- 一. 元组元组是Python中的一个内置的数据结构,它是一个不可变的序列,所谓的不可变序列就是不可以进行增删改的操作。1.1 元组的创建元组
- 仿射密码Affine Cipher是Multiplicative Cipher和Caesar Cipher算法的组合.仿射密码的基本实现如下
- 一、数据集下载加州高速公路PEMS数据集这里绘制PEMS04中的交通流量数据。该数据集中包含旧金山2018年1月1日至2月28日的29条道路
- 一、背景python是一个动态语言,可以支持我们在运行时动态的给类、对象添加属性或者方法;但是如果我们想要限制可以添加的属性或方法该怎么办呢
- 概述线性回归 (Linear Regression) 是利用回归分析来确定两种或两种以上变量间相互依赖的定量关系.对线性回归还不是很了解的同
- 一、输入注入注入攻击非常广泛而且很常见,注入有很多种类,它们影响所有的语言、框架和环境。SQL 注入是直接编写 SQL 查询(而非使用 OR