原生JS:Function对象(apply、call、bind)详解

Function对象(apply、call、bind)

本文参考MDN做的详细整理,方便大家参考[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)

Function 构造器会创建一个新的 Function 对象。 在 JavaScript 中每个函数都是一个Function对象。

构造器

new Function ([arg1[, arg2[, ...argN]],] functionBody)

  • arg1, arg2, ... arg被函数使用的参数的名称必须是有效的JavaScript标识符的字符串
  • functionBody 一个含有包括函数定义的JavaScript语句的字符串。
  • 以调用函数的方式调用Function的构造函数 (不是用new关键字) 跟以构造函数来调用是一样的.

例:var adder = new Function("a", "b", "return a + b"); // 创建了一个能返回两个参数和的函数

adder(2, 6); //  8

使用Function构造器生成的Function对象是在函数创建时解析的。这比你使用函数声明或者函数表达式(function)并在你的代码中调用更为低效,因为使用后者创建的函数是跟其他代码一起解析的。

所有被传递到构造函数中的参数,都将被视为将被创建的函数的参数,并且是相同的标示符名称和传递顺序。

使用Function构造器生成的函数,并不会在创建它们的上下文中创建闭包;它们一般在全局作用域中被创建。当运行这些函数的时候,它们只能访问自己的本地变量和全局变量,不能访问Function构造器被调用生成的上下文的作用域。这和使用带有函数表达式代码的 eval 不同。

属性:

Function.length

length 是函数对象的一个属性值,指明该函数期望多少个参数,即形参的个数。数量不包括剩余参数。相比之下,  arguments.length 是函数被调用时实际传参的个数。

  • Function 构造器本身也是个Function。他的 length 属性值为 1 。该属性 Writable: false, Enumerable: false, Configurable: true.
  • Function  原型对象的 length 属性值为 0 。

方法

Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])

在一个对象thisArg的上下文中应用另一个对象的方法;参数能够以列表形式传入,即在使用一个指定的this值(thisArg)和若干个指定的参数值的前提下调用某个函数或方法。

  • 需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。

call方法的应用:

  • 使用call方法调用函数并且指定上下文的‘this‘
function greet() {
  var reply = [this.person, ‘Is An Awesome‘, this.role].join(‘ ‘);
  console.log(reply);
}
var a = {
  person: ‘Douglas Crockford‘, role: ‘Javascript Developer‘
};
greet.call(a); // Douglas Crockford Is An Awesome Javascript Developer
  • 使用call方法调用父构造函数:在一个子构造函数中,你可以通过调用父构造函数的 call 方法来实现继承
function Food(name, price) {
  Product.call(this, name, price);
  this.category = ‘food‘;
}
  • 使用call方法调用匿名闭包函数,以达到绑定this和传参数的目的
for(var i =0;i<10;i++){
  setTimeout(function(){
    console.log(i);
  }.call(null,i),0)
}
//可以顺利输出0~9,如果不用call,则i的最终值,输出10个10

Function.prototype.apply(thisArg[, argsArray])

在一个对象的上下文中应用另一个对象的方法;参数能够以数组形式传入。

  • thisArg 在函数运行时指定的 this 值。需要注意的是,指定的 this 值并不一定是该函数执行时真正的 this 值,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。
  • argsArray 一个数组或者类数组对象(ES5新增,Chrome 14 以及 Internet Explorer 9 仍然不接受类数组对象。如果传入类数组对象,它们会抛出异常。),其中的数组元素将作为单独的参数传给函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。浏览器兼容性请参阅本文底部内容。
  • 使用 apply和call, 你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。
  • 可以使用 arguments 对象作为 argsArray 参数。 arguments 是一个函数的局部变量。 它可以被用作被调用对象的所有未指定的参数。 这样,你在使用apply函数的时候就不需要知道被调用对象的所有参数。 你可以使用arguments来把所有的参数传递给被调用对象。

使用apply和内置函数

聪明的apply用法允许你在某些本来需要写成遍历数组变量的任务中使用内建的函数。在接下里的例子中我们会使用Math.max/Math.min来找出一个数组中的最大/最小值。

var numbers = [5, 6, 2, 3, 7];
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
  • 当你对一个方法传入非常多的参数 (比如超过1W多个参数) 时, 就非常有可能会导致越界问题, 这个临界值是根据不同的 JavaScript 引擎而定的 (JavaScript 核心中已经做了硬编码 参数个数限制在65536),因为这个限制(实际上也是任何用到超大栈空间的行为的自然表现)是未指定的. 有些引擎会抛出异常.  更糟糕的是其他引擎会直接限制传入到方法的参数个数,导致参数丢失.
  • 如果你的参数数组可能非常大, 那么推荐使用下面这种策略来处理: 将参数数组切块后循环传入目标方法:
function minOfArray(arr) {
  var min = Infinity;
  var QUANTUM = 32768;
  for (var i = 0, len = arr.length; i < len; i += QUANTUM) {
    var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));
    min = Math.min(submin, min);
  }
  return min;
}
var min = minOfArray([5, 6, 2, 3, 7]);

Function.prototype.bind(thisArg[, arg1[, arg2[, ...]]])

bind()方法会创建一个新函数(新函数与被调函数具有相同的函数体)称为绑定函数。

  • 当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind()方法的第二个以及以后的参数作为绑定函数的预设参数,加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数.
  • 一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
function Point(x, y) {
  this.x = x;
  this.y = y;
}
Point.prototype.add = function() {
  return this.x + this.y;
};
var p = new Point(1, 2);
console.log(p); //Point { x: 1, y: 2 }
var YAxisPoint = Point.bind({}, 5);
var x = new YAxisPoint(6);
console.log(x); //Point { x: 5, y: 6 }
console.log(x.add()); //11 

bind用法:

bind() 最简单的用法是为原函数创建一个绑定函数,绑定到指定的对象上,使这个函数不论怎么调用都有同样的 this 值

this.x = 9;
var module = {
  x: 81,
  getX: function() { return this.x; }
};
module.getX(); // 返回 81
var retrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全部作用域
// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81

bind()的另一个最简单的用法是使一个函数拥有预设的初始参数

function list() {
  return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
var leadingThirtysevenList = list.bind(undefined, 37);
var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
function foo(x,y){
  return y ? x+y : foo.bind(void 0,x)
}
console.log(foo(1,4)); //3
console.log(foo(1)(4)); //3

配合 setTimeout,在默认情况下,使用 window.setTimeout() 时,this 关键字会指向 window (或全局)对象。当使用类的方法时,需要 this 引用类的实例,你可能需要显式地把 this 绑定到回调函数以便继续使用实例。

function LateBloomer() {
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
LateBloomer.prototype.bloom = function() {
  window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.declare = function() {
  console.log(‘I am a beautiful flower with ‘ +
  this.petalCount + ‘ petals!‘);
};
var flower = new LateBloomer();
flower.bloom(); // 一秒钟后, 调用\‘declare\‘方法

Function.prototype.toString()

获取函数的实现源码的字符串。覆盖了 Object.prototype.toString 方法。

时间: 2024-08-19 06:43:39

原生JS:Function对象(apply、call、bind)详解的相关文章

原生JS获取元素宽高实践详解

开篇的话 任何不是亲身实践中求得的知识,都不是属于你的. 任何求得的知识不去时常温习运用,也不是属于你的. 记录由来 在做个上拉广告功能中遇到了一个"理所当然"觉得对的用法,慢慢才排查出是获取元素高度那里出了问题,这个问题记忆在书上或视频中看到过,许久没用,用的时候就想当然了.遂决定深入剖析用法,增加记忆,记录发表出来,也能给各位前端同行.求学者增加印象. 出错之处 秉承结构.样式.行为分离的宗旨,每次不管大小案例都是分开写html.css.js.这也算抛砖引出了我犯错的玉. 原css

javascript中call、apply、bind详解

1.apply和call的区别在哪里 2.什么情况下用apply,什么情况下用call 3.apply的其他巧妙用法(一般在什么情况下可以使用apply) 我首先从网上查到关于apply和call的定义,然后用示例来解释这两个方法的意思和如何去用. apply:方法能劫持另外一个对象的方法,继承另外一个对象的属性. Function.apply(obj,args)方法能接收两个参数 obj:这个对象将代替Function类里this对象 args:这个是数组,它将作为参数传给Function(a

JavaScript原生对象属性和方法详解——Array对象 转载

length 设置或返回 数组中元素的数目. 注意:设置 length 属性可改变数组的大小.如果设置的值比其当前值小,数组将被截断,其尾部的元素将丢失.如果设置的值比它的当前值大,数组将增大,新的元素被添加到数组的尾部,它们的值为 undefined.所以length不一定代表数组的元素个数. var arr = new Array(3) arr[0] = "John" arr[1] = "Andy" arr[2] = "Wendy" cons

js对象浅拷贝和深拷贝详解

js对象浅拷贝和深拷贝详解 作者:i10630226 字体:[增加 减小] 类型:转载 时间:2016-09-05我要评论 这篇文章主要为大家详细介绍了JavaScript对象的浅拷贝和深拷贝代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文为大家分享了JavaScript对象的浅拷贝和深拷贝代码,供大家参考,具体内容如下 1.浅拷贝 拷贝就是把父对像的属性,全部拷贝给子对象. 下面这个函数,就是在做拷贝: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var

JS Function对象札记

Function对象 对象中的函数(方法) 当一个函数被保存为对象的一个属性时,我们称它为一个方法,当一个方法被调用时,this被绑定到该对象. 方法可以使用this访问自己所属的对象; return 一个函数纵使会返回一个值.如果没有指定返回值,则返回 undefined. 如果函数调用时在前面加上了 new 前缀,且返回值不是一个对象, 则返回 this(该新对象); 在jquery的on方法下,得到绑定函数的返回值; var returnValue; //通过变量来存储函数的返回值; fu

原生 JS 中对象相关 API 合集

https://juejin.im/entry/58f8a705a0bb9f0065a4cb20 原文链接:https://microzz.com/2017/04/20/jsobject/ 原生 JavaScript 中对象相关 API 合集 - 对象篇.现在 jQuery 已经没有那么有优势了,原生 JS 赶紧学起来... -- 由microzz分享 Microzz [email protected] 主页 文章 标签 GitHub 关于我 掘金专栏 SegmentFault Vue音乐播放器

OpenERP对象字段定义的详解

OpenERP对象支持的字段类型有,基础类型:char, text, boolean, integer, float, date, time, datetime, binary:复杂类型:selection, function, related:关系类型:one2one, one2many, many2one, many2many.下面逐一说明. boolean: 布尔型(true, false) integer: 整数. float: 浮点型,如 'rate' : fields.float('

Dream------scala--类的属性和对象私有字段实战详解

Scala类的属性和对象私有字段实战详解 一.类的属性 scala类的属性跟java有比较大的不同,需要注意的是对象的私有(private)字段 1.私有字段:字段必须初始化(当然即使不是私有字段也要赋值) 2.属性默认是public级别的,而且无法用public修饰. 3.可以有很多类,并且默认是public级别(如果声明的时候加上会报错,不知为何) 4.如果属性是public的,会默认生成类属性的getter和setter方法,无需显示的提供getter,setter方法 5.私有字段(用p

hibernate 对象关系映射文件详解

POJO 类和数据库的映射文件*.hbm.xml POJO类和关系数据库之间的映射可以用一个XML文档来定义. 映射文件的扩展名为.hbm.xml 在运行时Hibernate将根据这个映射文件来生成各种SQL语句 通过POJO类的数据库映射文件,Hibernate可以理解持久化类和数据表之间的对应关系,也可以理解持久化类属性与数据库表列之间的对应关系 映射文件说明 hibernate-mapping 类层次:class 主键:id 基本类型:property 实体引用类: many-to-one