首页 > 编程语言 >JavaScript之applye、bind和call方法详解

JavaScript之applye、bind和call方法详解

时间:2024-04-08 13:29:05浏览次数:17  
标签:console log bind JavaScript call apply prototype name

Question

  • Q1 apply()、bind()和call()方法的区别在哪?

  • Q2 apply()和call()的应用场景

  • Q3 apply()、bind()和call()方法手写实现逻辑

来源

继承自Function.prototype,属于实例方法

console.log(Function.prototype.hasOwnProperty('call')) //true
console.log(Function.prototype.hasOwnProperty('apply')) //true
console.log(Function.prototype.hasOwnProperty('bind')) //true

定义

apply()、bind()和call()方法的作用都是改变this指向,可以指定该函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。

一般用法

// 限浏览器Console执行
var year = 2022 // 不能用let
function getDate(month, day) {
  console.log(this.year + '-' + month + '-' + day)
}

let obj = { year: 2023 }
getDate.call(this, 1, 1)    //2022-1-1 this|null|undefined均可
getDate.call(obj, 1, 1)     //2023-1-1
getDate.apply(obj, [1, 1])  //2023-1-1
getDate.bind(obj)(1, 1)     //2023-1-1

区别

  • apply()的参数为数组

  • apply()和call()立即执行,bind()返回新函数,并非立即执行

apply()

找出最大值和最小值
var arr = [5, 6, 2, 3, 7]
console.log(Math.max.apply(null, arr))
console.log(Math.min.apply(null, arr))

将数组的空元素变为undefined
// 空元素与undefined的差别在于,数组的forEach方法会跳过空元素,但是不会跳过undefined和null。因此,遍历内部元素的时候,会得到不同的结果
console.log(Array.apply(null, [1, , 3])) // [1, undefined, 3]
转换类似数组的对象
function keith(a, b, c) {
  return arguments
}

console.log(Array.prototype.slice.apply(keith(2, 3, 4)))    //[2,3,4]
console.log(Array.prototype.slice.call(keith(2, 3, 4)))   //[2,3,4]

bind()

var func = {
  a: 1,
  count: function () {
    console.log(this.a++)
  }
}

var ff = func.count.bind(func)

ff()

call()

调用对象的原生方法
var obj = {}
console.log(obj.hasOwnProperty('toString')) //false

obj.hasOwnProperty = function () {
  return true
}

console.log(obj.hasOwnProperty('toString')) //true

console.log(Object.prototype.hasOwnProperty.call(obj, 'toString')) //false

调用父构造函数
function Product(name, price){
  this.name = name
  this.price = price
}

// 调用父构造函数的call方法来实现继承
function Food(name, price){
  Product.call(this, name, price)
  this.category = 'food'
}

function Toy(name, price){
  Product.call(this, name, price)
  this.category = 'toy'
}

var cheese = new Food('feta', 5)
var fun = new Toy('robot', 40)

console.log(cheese)
console.log(fun)

手写

apply()
/**
 * 模拟 apply
 * 调用一个具有给定 this 值的函数,以及以一个数组(或类数组对象)的形式提供的参数
 * @param {object} ctx
 * @param {} args
 */
Function.prototype.__apply = function (ctx, args) {
    if (typeof this !== 'function') throw new TypeError('Error')

    // 考虑 null 情况,参数默认赋值会无效
    if (!ctx) ctx = window

    // 将 this 函数保存在 ctx 上
    ctx.fn = this

    // 传参执行并保存返回值
    const result = ctx.fn(...args)

    // 删除 ctx 上的 fn
    delete ctx.fn
  
    return result
}

// ------------------------------ 测试 ------------------------------

const numbers = [5, 6, 2, 3, 7]

// Function.prototype.__apply()
console.log('Function.prototype.__apply()')

const max = Math.max.__apply(null, numbers)
console.log(max) // 7

// Function.prototype.apply()
console.log('Function.prototype.apply()')
const min = Math.min.apply(null, numbers)
console.log(min) // 2
bind()
/**
 * 1. bind() 方法创建一个新的函数
 * 2. 在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数
 * 3. new 情况下忽略第一个参数
 * 4. 其余参数将作为新函数的参数,供调用时使用
 * @param {object} ctx
 * @param  {...any} args
 * @returns {function} 返回一个原函数的拷贝,并拥有指定 this 值和初始参数
 */
Function.prototype.__bind = function (ctx, ...args) {
    // 判断 this 是否为 function 类型
    if (typeof this !== 'function') throw new TypeError('Error')

    // 保存当前 this
    const __this = this

    return function F() {
        return this instanceof F
            ? new __this(...args, ...arguments) // new 
            : __this.apply(ctx, [...args, ...arguments]) // 直接调用时绑定 this
    }
}

// ------------------------------ 测试 ------------------------------

function print() {
    console.log(this.name, ...arguments)
}

const obj = {
    name: 'mxin',
}

// Function.prototype.__bind()
console.log('Function.prototype.__bind()')

// 直接调用,返回原函数拷贝,this 指向 obj
const F = print.__bind(obj, 26)
F(178) // mxin, 26, 178

// new 情况
const _obj = new F(145) // undefined, 26, 145
console.log(_obj) // print {}

// Function.prototype.bind()
console.log('Function.prototype.bind()')

const Fn = print.bind(obj, 26)
Fn(178) // mxin, 26, 178

const __obj = new Fn(145) // undefined, 26, 145
console.log(__obj) // print {}
call()
/**
 * 模拟 call
 * 使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数
 * @param {object} ctx
 * @param  {...any} args
 * @returns {any} 调用 this 的返回值,若无有返回值,则返回 undefined
 */
Function.prototype.__call = function (ctx, ...args) {
    if (typeof this !== 'function') throw new TypeError('Error')

    // 考虑 null 情况,参数默认赋值会无效
    if (!ctx) ctx = window

    // 将 this 函数保存在 ctx 上
    ctx.fn = this

    // 传参执行并保存返回值
    const res = ctx.fn(...args)

    // 删除 ctx 上的 fn
    delete ctx.fn
  
    return res
}

// ------------------------------ 测试 ------------------------------

function Product(name, price) {
    this.name = name
    this.price = price
}

// Function.prototype.__call()
console.log('Function.prototype.__call()')

function Food(name, price) {
    Product.__call(this, name, price)
    this.category = 'food'
}
const food = new Food('cheese', 5)
console.log(food)
// Food {name: "cheese", price: 5, category: "food"}
//   category: "food"
//   name: "cheese"
//   price: 5
//   __proto__:
//     constructor: ƒ Food(name, price)
//     __proto__: Object

// Function.prototype.call()
console.log('Function.prototype.call()')

function Toy(name, price) {
    Product.call(this, name, price)
    this.category = 'toy'
}
const toy = new Toy('car', 10)
console.log(toy)
// Toy {name: "car", price: 10, category: "toy"}
//   category: "toy"
//   name: "car"
//   price: 10
//   __proto__:
//     constructor: ƒ Toy(name, price)
//     __proto__: Object

本文由 mdnice 多平台发布

标签:console,log,bind,JavaScript,call,apply,prototype,name
From: https://blog.csdn.net/Jarvan_I/article/details/137378202

相关文章

  • 前端学习<四>JavaScript基础——11-流程控制语句:选择结构(if和switch)
    代码块用{}包围起来的代码,就是代码块。在ES5语法中,代码块,只具有分组的作用,没有其他的用途。代码块中的内容,在外部是完全可见的。举例: {   vara=2;   alert('qianguyihao');   console.log('千古壹号'); } ​ console.log('a='+a);打印结......
  • 前端学习<四>JavaScript基础——10-运算符
    我们在前面讲过变量,本文讲一下运算符和表达式。运算符的定义和分类运算符的定义运算符:也叫操作符,是一种符号。通过运算符可以对一个或多个值进行运算,并获取运算结果。表达式:数字、运算符、变量的组合(组成的式子)。表达式最终都会有一个运算结果,我们将这个结果称为表达式的......
  • JavaScript性能优化
    JavaScript性能优化JavaScript是前端开发中最常用的脚本语言之一,优化它的性能可以有效地提升页面响应速度和流畅度。下面介绍一些常见的JavaScript性能优化方法: 1.函数节流函数节流是指在一定时间内只执行一次函数,比如鼠标滚动事件,如果没有限制,那么每次鼠标滚动都会触发大量......
  • 使用JavaScript设置Tab栏自动切换
    在下面代码中设置了tab栏可以进行周期性的切换时间时5秒,也可以鼠标移到相应的位置进行切换。在允许过程中出现:Cannotreadpropertiesofundefined(reading'className')报错,不知道时什么原因还没有解决,希望大佬来指点一下!!!!<!DOCTYPEhtml><htmllang="en"><head><meta......
  • Apr.7.2024小结——汇编中jmp和call的用法
    今天终于跑起来了自己OS的mbr,还是很激动人心的。学习了16位实模式下jmp和call的各种用法,来总结一下:call(near)0xabcd相对近调用后面的地址是相对的-32768~32767call[addr]间接绝对近调用地址为绝对,但是是在某个寄存器或内存中call(far)段基址:偏移直接绝对远调用跨......
  • 如何在JavaScript中解析S表达式
    S表达式是Lisp编程语言家族的基础。在本文中,我将逐步向您展示如何创建一个简单的S表达式解析器。这可以作为Lisp解析器的基础。Lisp是实现最简单的语言之一,创建解析器是第一步。我们可以使用解析器生成器来完成这项任务,但自己编写解析器会更容易。我们将使用JavaScript。(本文内......
  • javascript 原生JS实现 fadeIn / fadeOut 方法
    js源码:Object.prototype.fadeIn=function(time,callback){varel=this;el.style.opacity=0;varst=setInterval(function(){el.style.opacity=parseFloat(el.style.opacity)+0.01;if(el.style.opacity>=1){clearInterval(st);if(callback!==......
  • 【Web应用技术基础】JavaScript(8)——案例:待办事项
    视频已发。截图如下:<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>Document<......
  • 前端【VUE】02-vue指令【v-html 、v-show、 v-if 、v-else、v-on、v-bind、v-for、v-m
    Vue指令①v-html1<!DOCTYPEhtml>2<htmllang="en">3<head>4<metacharset="UTF-8">5<metahttp-equiv="X-UA-Compatible"content="IE=edge">6<metaname="view......
  • 基于 bindview.js + node + mysql 的简易记账系统
    该项目前端是基于bindview.js和bootcss,后端的基于node.js使用了express框架,数据库使用的mysql8 bindview: bronze-ding/bindview:Bindview.js,是一个使用虚拟DOM来创建真实DOM并提供了数据响应式的Javascript工具库(github.com)功能包括:增加,删除,修改,查询登录......