6.函数是特殊的对象2 - JS

这篇具有很好参考价值的文章主要介绍了6.函数是特殊的对象2 - JS。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在第一部分中,主要总结了函数作为一个对象的常见属性(name/length)、如何自定义属性以及如何使用函数构造器(Function)。

这里总结函数作为对象的常见方法(apply/call/bind/toString)。


使用 call 绑定 this

call 方法指定 this 并且逐个提供参数。

基本语法

  • thisArg,调用 f 时指定的 this 值:
    • 非严格模式下,null/undefined 将被替换为全局对象,
    • 原始值(String/Number/...)将被转换为对象;
  • arg1, arg2, /* …, */ argN 函数的参数,逐个传入。
f.call(thisArg)
f.call(thisArg, arg1)
f.call(thisArg, arg1, arg2)
f.call(thisArg, arg1, arg2, /* …, */ argN)

简单案例

下面一个案例:普通调用时,this 指向全局;call 调用时,this 指向具体对象。

function log() {
	console.log(this.xxx);
	console.log(this);
}
let o = { xxx: 100, yyy: 200 }

log();			// undefined或其他(globalThis.xxx) - globalThis/Window
log.call(o)		// 100 - { xxx: 100, yyy: 200 }
log.call()		// undefined或其他(globalThis.xxx) - globalThis/Window
log.call(null)	// undefined或其他(globalThis.xxx) - globalThis/Window

上面的例子中,undefined/null 将转换成全局对象,但在严格模式下:

"use strict"
function log() {
	console.log(this);
}

log.call()			// undefined
log.call(null)		// null

借用方法

一个对象的方法,给另外一个对象使用。达到目的方案如下:

  1. 直接通过对象调用,并且通过 call 指定其他的对象;
  2. 先将方法赋值成为一个函数,在通过 call 的调用指定其他的对象;
  3. 先将方法赋值成为一个函数,并对 call 进行自绑定。
const cat = {
	name: 'Kitty',
	showName () { console.log(this.name); }
}
const dog = {
	name: '阿旺'
}

cat.showName.call(dog)			// '阿旺'

let showName = cat.showName
showName.call(dog)				// '阿旺'

let boundedShowName = showName.call.bind(showName)
boundedShowName(dog)			// '阿旺'

对象原型链上的方法也可以借用。下面一个例子中,借用数组原型链上的切片方法。

let arr = [1, 2, 3, 4];
const slice = Array.prototype.slice;
const boundedSlice = slice.call.bind(slice);

slice.call(arr, 1)		// [2, 3, 4]
boundedSlice(arr, 1)	// [2, 3, 4]

apply 方法

applycall 方法能够达到几乎等效目的。

基本语法

  • thisArg,调用 f 时指定的 this 值:
    • 非严格模式下,null/undefined 将被替换为全局对象,
    • 原始值(String/Number/...)将被转换为对象;
  • argsArray,类数组对象,调用 f 时的参数(或者如果不需要向函数提供参数,则为 null/undefined)。
f.apply(thisArg)
f.apply(thisArg, argsArray)

apply VS call

大多数情况下,使用 callapply 均可,apply 可能会更快,因为大多数 JS 引擎在内部对其进行了优化。下面一个等效表达中:

  • arr 期望是一个类数组对象;
  • itor 期望是一个可迭代对象。
f.apply(thisArg, arr)
f.call(thisArg, ...itor)

可以看出,apply/call 两者几乎相同,不同的是参数方式,参数在 call() 中逐个作为列表传递,而在 apply() 中它们会组合在一个类数组对象中。

Math.max.apply(null, [1, 3, 5])		// 5
Math.max.call(null, 1, 3, 2)		// 3

简单案例

下面一个例子中:在一个数组后添加若干元素。

let arr = ["a", "b"];
arr.push.apply(arr, [0, 1, 2]);
arr				// ["a", "b", 0, 1, 2]

arr.push.call(arr, 3, 4)
arr				// ["a", "b", 0, 1, 2, 3, 4]

arr.push(arr, ...[5, 6])
arr				// ["a", "b", 0, 1, 2, 3, 4, 5, 6]

呼叫转移

将所有参数、上下文一起传递给另一个函数被称为“呼叫转移”。下面是一种最简单的表达,在外部看来 f()w() 没有声明区别。

function f() { console.log(arguments[0]) }
function w() { f.apply(this, arguments) }

f()		// 1
w()		// 1

使用 bind 创建一个 Bounded Function - 绑定函数

bind 方法创建一个新函数,当调用该新函数时,它会调用原始函数(目标函数-Target Function):

  • 将其 this 上下文绑定为指定值;
  • 同时,还可以绑定一系列参数(会插到新函数传入参数的前面)。

基本语法

  • thisArg,调用绑定函数给原始函数 f 时指定的 this 值:
    • 非严格模式下,null/undefined 将被替换为全局对象,
    • 原始值(String/Number/...)将被转换为对象,
    • 如果使用 new 创建绑定函数,忽略该值;
  • arg1, arg2, /* …, */ argN,在调用原始函数 f 时,逐个插入到传入绑定函数的参数前的参数。。
f.bind(thisArg)
f.bind(thisArg, arg1)
f.bind(thisArg, arg1, arg2)
f.bind(thisArg, arg1, arg2, /* …, */ argN)

bind 参数的绑定顺序

给函数指定 this 值,并不复杂。下面一个案例具体讲述 bind 参数的绑定顺序问题。

  • boundedFn1 成功地绑定了 this 对象 为一个字符串 xy,并且将两个其余参数绑定,在调用时:
    • 目标函数 fnthis 指定为 xy
    • 通过 bind 绑定的其他参数插入在目标函数参数列表的前面,相当于 fn('a', 'b', '1', '2')
  • boundedFn2 失败地绑定了 this 对象 为一个字符串 xxyy,并且将两个其余参数绑定,在调用时:
    • 目标函数 fnthis 指定仍然xy(因为 boundedFn1 已经是一个绑定函数,this 的指定不会被覆盖);
    • 通过 bind 绑定的其他参数插入在目标函数参数列表的前面,以及第一次绑定参数的后面,相当于 fn('a', 'b', 'c', 'd', '1', '2')
function fn(...args) {
	console.log(this, args.join(''));
}

let boundedFn1 = fn.bind('xy', 'a', 'b')
boundedFn1('1', '2')		// String {'xy'} 'ab12'

let boundedFn2 = boundedFn1.bind('xxyy', 'c', 'd')
boundedFn2('1', '2')		// String {'xy'} 'abcd12'

一般的,对于多层绑定函数,this 指定是第一层;其他参数按照层数,依次插入在目标函数的参数列表前面(fn(...argsFloor1, /* ... */, argsFloorX, ...args))。

使用 toString 方法获取函数源码字符串

  1. 函数作为对象,重写了从 Object 继承来的 toString() 方法。toString 方法返回一个包含用于定义函数的源文本段的字符串。
  2. 对于内置函数、由 bind 创建的绑定函数、非 JavaScript 函数,调用 toString(),返回一个看起来像原函数的字符串。
function f() {
	console.log(111)
}

f.toString()				// 'function g() { \n    console.log(111) \n}'
g.bind(null).toString()		// 'function () { [native code] }'
Math.max.toString()			// 'function max() { [native code] }'

实际源代码与 toString() 结果比较

注意:toString 方法可以修改。

function f() { }
class A {
  	a() { }
}
function* g() {	}

f.toString()			// "function f() { }"			普通函数
A.toString()			// "class A { a() { } }"		类
g.toString()			// "function* g() { }"			生成器
((a) => a).toString()		// "(a) => a"				箭头函数
({ a() {} }.a).toString()	// "a() {}"					方法
({ *a() {} }.a).toString()  // "*a() {}"				生成器方法
({ [x]() {} }[x]).toString()// "[x]() {}"				计算方法
Function.prototype.toString.toString()	// "function toString() { [native code] }"
f.bind(null).toString()					// "function () { [native code] }"
(Function("a", "b")).toString()			// function anonymous(a\n) {\nb\n}
(Object.getOwnPropertyDescriptor({ get a() {} }, "a").get).toString()	// "get a() {}"
(Object.getOwnPropertyDescriptor({ set a(x) {} }, "a").set).toString()	// "set a(x) {}"

关于什么的计算方法/属性:计算变量不能点式读取。文章来源地址https://www.toymoban.com/news/detail-826006.html

let [x, y, z] = ['ad', 'cc', 'gg']
let o = { [x]: 11, [y]: function() {}, [z] (){} }

o.ad	// 11
o.cc	// f () {}
o.gg	// ƒ [z](){}
o[x]	// 11
o[y]	// f () {}
o[z]	// ƒ [z](){}
o.x		// undefined
o.y		// undefined
o.z		// undefined

到了这里,关于6.函数是特殊的对象2 - JS的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 30个前端开发中常用的JavaScript函数

    🧑‍💻作者名称:DaenCode 🎤作者简介:啥技术都喜欢捣鼓捣鼓,喜欢分享技术、经验、生活。 😎人生感悟:尝尽人生百味,方知世间冷暖。 在前端开发中通常会用到校验函数,检验是否为空、手机号格式、身份证格式等等。现按照用途分类整理出了30个常用的方法,在V

    2024年02月14日
    浏览(34)
  • JS-27 前端数据请求方式;HTTP协议的解析;JavaScript XHR、Fetch的数据请求与响应函数;前端文件上传XHR、Fetch;安装浏览器插件FeHelper

    早期的网页都是通过后端渲染来完成的,即服务器端渲染(SSR,server side render): 客户端发出请求 - 服务端接收请求并返回相应HTML文档 - 页面刷新,客户端加载新的HTML文档; 服务器端渲染的缺点: 当用户点击页面中的某个按钮向服务器发送请求时,页面本质上只是一些数

    2024年02月16日
    浏览(48)
  • 蓝旭前端05:JavaScript进阶

    数据类型 基本数据类型:Number、String、Boolean、Null、Undefined等。 引用数据类型:Object、Array、Function等。 typeof操作符:返回数据类型的字符串形式。 变量 变量声明:var、let、const。区别:var没有块级作用域,let和const有块级作用域,const声明的变量不能修改。什么是块级作用

    2024年04月26日
    浏览(29)
  • js 对象 进阶

    我们之前已经学习了如何创建一个对象,那我们要是想要创建多个对象又该怎么办?聪明的同学可能会说,直接在写几个对象不就好了吗?比如下边的代码: 的确,上述代码确实可以创建多个对象,但是,这样的解决方案真的好吗?对于少量对象可能使用,我们假设说,要用

    2023年04月09日
    浏览(15)
  • JavaScript:js数组/对象遍历方法

    一、js遍历方法 序号 方法 描述 1 for 使用最基本的for循环可以遍历数组 2 for of for...of语句用来遍历可迭代对象(包括数组、Set、Map、字符串等),它可以替代传统的for循环和forEach()方法。for...of循环每次迭代都将返回一个值,而不是索引。 3 for in for...in语句用来遍历对象的可

    2024年02月09日
    浏览(44)
  • 【JavaScript笔记】面对对象与构造函数

    了解面向对象编程中的一般概念 能够基于构造函数创建对象 理解 JavaScript 中一切皆对象的语言特征 理解引用对象类型值存储的的特征 掌握包装类型对象常见方法的使用 了解面向对象的基础概念,能够利用构造函数创建对象。 1. 构造函数 构造函数是专门用于创建对象的函

    2024年02月11日
    浏览(30)
  • 【JavaScript】对象 ② ( 对象使用 | 调用对象属性 | 调用对象方法 | 变量与属性区别 | 函数与方法区别 )

    使用字面量创建对象要点 : 在上一篇博客 【JavaScript】对象 ① ( 对象概念 | 对象使用场景 | 使用字面量创建对象 | 空对象字面量 | 小括号 / 中括号 / 大括号 作用 ) 中 , 介绍了 使用 字面量 创建对象 , 有如下要点 : 键值对 : 对象字面量 中的 属性 和 方法 都是以 \\\" 键值对 \\\" 的形

    2024年04月14日
    浏览(37)
  • Web 前端进阶—— JS 学习笔记

    目录 一、JavaScript提升篇 1、什么是跨域? 2、什么是原型? 3、什么是闭包? 4、如何防抖? 5、TCP的三次握手和四次挥手 6、new 操作符原理 7、事件委托做了什么 8、事件代理是什么 9、Eventloop 10、 如何实现跨域 11、写出原生 Ajax 12、暂时性死区是什么 13 、promise 解决回调陷阱的

    2024年04月26日
    浏览(24)
  • 转换json格式的日期为Javascript对象的函数

    项目中碰到了用jQuery从后台获取的json格式的日期的字符串,需要将此字符串转换成JavaScript的日期对象。 代码如下: 开发中有时候需要从服务器端返回json格式的数据,在后台代码中如果有DateTime类型的数据使用系统自带的工具类序列化后将得到一个很长的数字表示日期数据,

    2023年04月25日
    浏览(30)
  • 前端学习——JS进阶 (Day1)

    局部作用域 全局作用域 作用域链 JS垃圾回收机制 闭包 变量提升 函数提升 函数参数 动态参数 剩余参数 展开运算符 箭头函数(重要) 基本写法 箭头函数参数 箭头函数 this 数组解构 练习 数组解构 对象解构 多级对象解构 for each 案例 筛选

    2024年02月16日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包