JavaScript Function相关属性和方法

2019-05-07  本文已影响0人  Kevin丶CK

Function 构造函数创建一个新的Function对象。 在 JavaScript 中, 每个函数实际上都是一个Function对象。

语法

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

参数

arg1, arg2, ... argN
被函数使用的参数的名称必须是合法命名的。参数名称是一个有效的JavaScript标识符的字符串,或者一个用逗号分隔的有效字符串的列表;例如“×”,“theValue”,或“A,B”。
functionBody
一个含有包括函数定义的JavaScript语句的字符串。

描述

使用Function构造器生成的Function对象是在函数创建时解析的。这比你使用函数声明或者函数表达式(function)并在你的代码中调用更为低效,因为使用后者创建的函数是跟其他代码一起解析的。
所有被传递到构造函数中的参数,都将被视为将被创建的函数的参数,并且是相同的标示符名称和传递顺序。
以调用函数的方式调用Function的构造函数 (不是用new关键字) 跟以构造函数来调用是一样的。

属性和方法

全局的Function对象没有自己的属性和方法, 但是, 因为它本身也是函数,所以它也会通过原型链从Function.prototype上继承部分属性和方法。

原型对象

Function.arguments
以数组形式获取传入函数的所有参数。此属性已被arguments替代。(该特性已经从 Web 标准中删除)
Function.caller
获取调用函数的具体对象。(非标准)
Function.length
获取函数的接收参数个数。
Function.displayName
获取函数的display name。(非标准)
Function.prototype.constructor
声明函数的原型构造方法
***示例
1.传入参数调用Function构造函数

      // 创建了一个能返回两个参数和的函数
      const adder = new Function("a", "b", "return a + b");
      // 调用函数
     console.log(adder(2, 6));//8

参数"a"和"b"是参数的名字,在函数体中被使用,"return a + b"。
几乎很少看到这样做的,这比使用函数声明或者函数表达式(function)并在你的代码中调用更为低效。
2.Function构造器生成的函数,在全局作用域中被创建

 // 1、f()函数返回的function e()是闭包.
      var n = 1;
      function f() {
        var n = 2;
        function e() {
          return n;
        }
        return e;
      }
      console.log(f()()); //2

      // 2、f()函数返回的function e()是全局作用域函数
      var n = 1;
      function f() {
        var n = 2;
        var e = new Function("return n;");
        return e;
      }
      console.log(f()()); //1

方法

1、Function​.prototype​.apply()

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。
注意:call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。

1.1语法

func.apply(thisArg, [argsArray])

1.2参数

thisArg
可选的。在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
argsArray
可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。 浏览器兼容性 请参阅本文底部内容。

1.3返回值

调用有指定this值和参数的函数的结果

1.4描述

在调用一个存在的函数时,你可以为其指定一个 this 对象。 this 指当前对象,也就是正在调用这个函数的对象。 使用 apply, 你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。

apply 与 call() 非常相似,不同之处在于提供参数的方式。apply 使用参数数组而不是一组参数列表。apply 可以使用数组字面量(array literal),如 fun.apply(this, ['eat', 'bananas']),或数组对象, 如 fun.apply(this, new Array('eat', 'bananas'))。

你也可以使用 arguments对象作为 argsArray 参数。 arguments 是一个函数的局部变量。 它可以被用作被调用对象的所有未指定的参数。 这样,你在使用apply函数的时候就不需要知道被调用对象的所有参数。 你可以使用arguments来把所有的参数传递给被调用对象。 被调用对象接下来就负责处理这些参数。

从 ECMAScript 第5版开始,可以使用任何种类的类数组对象,就是说只要有一个 length 属性和(0..length-1)范围的整数属性。例如现在可以使用 NodeList 或一个自己定义的类似 {'length': 2, '0': 'eat', '1': 'bananas'} 形式的对象。

1.5示例

用 apply 将数组添加到另一个数组

      let array = ["a", "b"];
      let elements = [0, 1, 2];
      let newArr = array.concat.apply(array, elements);
      console.log(newArr); // ["a", "b", 0, 1, 2]
      array.push.apply(array, elements);
      console.log(array); // ["a", "b", 0, 1, 2]

使用apply和内置函数
聪明的apply用法允许你在某些本来需要写成遍历数组变量的任务中使用内建的函数

      /* 找出数组中最大/小的数字 */
      let numbers = [5, 6, 2, 3, 7];
      /* 应用(apply) Math.min/Math.max 内置函数完成 */
      let max = Math.max.apply(null, numbers);
      console.log(max);
      /* 基本等同于 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..) */
      let min = Math.min.apply(null, numbers);
      console.log(min);

JavaScript引擎的参数长度是有限制的,所以使用apply得注意参数的个数。可以采用将参数数组切块后循环传入目标方法:

      function minOfArray(arr) {
        let min = Infinity;
        let QUANTUM = 2;
        
        for (let i = 0, len = arr.length; i < len; i += QUANTUM) {
          let 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]);
      console.log("min:"+min); //min:2

2、Function​.prototype​.call()

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

3.1语法

fun.call(thisArg, arg1, arg2, ...)

3.2参数

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

3.3返回值

使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined。

3.4描述

call() 允许为不同的对象分配和调用属于一个对象的函数/方法。
call() 提供新的 this 值给当前调用的函数/方法。你可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。

3.5示例

1.使用 call 方法调用父构造函数
在一个子构造函数中,你可以通过调用父构造函数的 call 方法来实现继承,类似于 Java 中的写法。下例中,使用 Food 和 Toy 构造函数创建的对象实例都会拥有在 Product 构造函数中添加的 name 属性和 price 属性,但 category 属性是在各自的构造函数中定义的。

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

      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);
      console.log(cheese.name + "的价格为" + cheese.price + "属于" + cheese.category);//feta的价格为5属于food

      var fun = new Toy("robot", 40);
      console.log(fun.name + "的价格为" + fun.price + "属于" + fun.category);//robot的价格为40属于toy

2.使用 call 方法调用匿名函数
在下例中的 for 循环体内,我们创建了一个匿名函数,然后通过调用该函数的 call 方法,将每个数组元素作为指定的 this 值执行了那个匿名函数。这个匿名函数的主要目的是给每个数组元素对象添加一个 print 方法,这个 print 方法可以打印出各元素在数组中的正确索引号。

      let animals = [
        { species: "Lion", name: "King" },
        { species: "Whale", name: "Fail" }
      ];

      for (let i = 0; i < animals.length; i++) {
        (function(i) {
          this.print = function() {
            console.log(i + "项:" + this.species + "_" + this.name);
          };
          this.print();
        }.call(animals[i], i));
       //0项:Lion_King
       //1项:Whale_Fail
      }

当然,这里不是必须得让数组元素作为 this 值传入那个匿名函数(普通参数就可以),目的是为了演示 call 的用法。

3.使用 call 方法调用函数并且指定上下文的 this

      function greet() {
        let reply = [
          this.animal,
          "typically sleep between",
          this.sleepDuration
        ].join(" ");
        console.log(reply);
      }

      let obj = {
        animal: "cats",
        sleepDuration: "12 and 16 hours"
      };

      greet.call(obj); // cats typically sleep between 12 and 16 hours

例子中,当调用 greet 方法的时候,该方法的this值会绑定到 obj 对象。

4.使用 call 方法调用函数并且不指定第一个参数(argument)

      var sData = "white";
      function display() {
        console.log("sData value is %s ", this.sData);
      }

      display.call({sData:'yellow'}); // sData value is yellow
      display.call(); // sData value is white

3、Function​.prototype​.bind()

bind()方法创建一个新的函数,在调用时设置this关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。

3.1语法

function.bind(thisArg[, arg1[, arg2[, ...]]])

3.2参数

thisArg
调用绑定函数时作为this参数传递给目标函数的值。
如果使用new(new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例)运算符构造绑定函数,则忽略该值。当使用bindsetTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object。如果bind函数的参数列表为空,执行作用域的this将被视为新函数的thisArg
arg1, arg2, ...
当目标函数被调用时,预先添加到绑定函数的参数列表中的参数。

3.3返回值

返回一个原函数的拷贝,并拥有指定的this值和初始参数。

3.4描述

bind() 函数会创建一个新绑定函数(bound function,BF)。绑定函数是一个exotic function object(怪异函数对象,ECMAScript 2015中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数
绑定函数具有以下内部属性:

当调用绑定函数时,它调用[[BoundTargetFunction]]上的内部方法[[Call]],就像这样Call(boundThis, args)。其中,boundThis[[BoundThis]]args[[BoundArguments]]**加上通过函数调用传入的参数列表。

绑定函数也可以使用new运算符构造,它会表现为目标函数已经被构建完毕了似的。提供的this值会被忽略,但前置参数仍会提供给模拟函数。

3.5示例

1.创建绑定函数
bind() 最简单的用法是创建一个函数,不论怎么调用,这个函数都有同样的 this 值。JavaScript新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,期望方法中的 this 是原来的对象(比如在回调中传入这个方法)。如果不做特殊处理的话,一般会丢失原来的对象。基于这个函数,用原始的对象创建一个绑定函数,巧妙地解决了这个问题:

      var x = 9; // 在浏览器中,this指向全局的 "window" 对象
      var module = {
        x: 81,
        getX: function() {
          return this.x;
        }
      };
      console.log(module.getX()); // 81

      var retrieveX = module.getX;
       // 返回9 - 因为函数是在全局作用域中调用的
      console.log(retrieveX());//9
     
      // 创建一个新函数,把 'this' 绑定到 module 对象
      // 新手可能会将全局变量 x 与 module 的属性 x 混淆
      var boundGetX = retrieveX.bind(module);
      console.log(boundGetX()); // 81

2.偏函数
bind()的另一个最简单的用法是使一个函数拥有预设的初始参数。只要将这些参数(如果有的话)作为bind()的参数写在this后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。

function list() {
        return Array.prototype.slice.call(arguments);
      }

      function addArguments(arg1, arg2) {
        return arg1 + arg2;
      }

      let list1 = list(1, 2, 3); // [1, 2, 3]
      console.log(list1);

      let result1 = addArguments(1, 2); // 3
      console.log(result1);

      // 创建一个函数,它拥有预设的第一个参数
      let leadingThirtySevenList = list.bind(null, 37);
      let list2 = leadingThirtySevenList();
      console.log(list2); // [37]

      // 创建一个函数,它拥有预设的第一个参数
      let addThirtySeven = addArguments.bind(null, 37);
      let list3 = leadingThirtySevenList(1, 2, 3);
      console.log(list3); // [37, 1, 2, 3]

      //let 37 + 5 = 42
      var result2 = addThirtySeven(5);
      console.log(result2); // 37 + 5 = 42

      //已经有一个参数37,所以第二个10会忽略掉
      var result3 = addThirtySeven(5, 10);
      console.log(result3); // 37 + 5 = 42

3.配合 setTimeout
在默认情况下,使用 window.setTimeout() 时,this 关键字会指向 window (或global)对象。当类的方法中需要 this 指向类的实例时,你可能需要显式地把 this 绑定到回调函数,就不会丢失该实例的引用。

      function LateBloomer() {
        this.petalCount = Math.ceil(Math.random() * 30) + 1;
      }
      // 在 1 秒钟后声明 bloom
      LateBloomer.prototype.bloom = function() {
        window.setTimeout(this.declare.bind(this), 1000);
      };

      LateBloomer.prototype.declare = function() {
        console.log(
          "I am a IT, " + this.petalCount  + " olds!"
        );
      };

      var flower = new LateBloomer();
      flower.bloom(); // 一秒钟后, 调用'declare'方法

4.作为构造函数使用的绑定函数绑定函数自动适应于使用 new操作符去构造一个由目标函数创建的新实例。当一个绑定函数是用来构建一个值的,原来提供的 this 就会被忽略。不过提供的参数列表仍然会插入到构造函数调用时的参数列表之前。

      function Point(x, y) {
        this.x = x;
        this.y = y;
      }
      Point.prototype.toString = function() {
        return this.x + "," + this.y;
      };
      let YAxisPoint = Point.bind(null, 0);
      let axisPoint = new YAxisPoint(5);
      console.log(axisPoint.x); // 0
      console.log(axisPoint.y); // 5
      console.log(axisPoint.toString()); // '0,5'

不需要做特别的处理就可以用 new操作符创建一个绑定函数。也就是说,不需要做特别处理就可以创建一个可以被直接调用的绑定函数,即使你更希望绑定函数是用 new操作符来调用。

      var emptyObj = {};
      var mYAxisPoint = Point.bind(emptyObj, 0);
      // 仍然能作为一个普通函数来调用
      mYAxisPoint(13);
      console.log(emptyObj.x + ',' + emptyObj.y);//0,13

如果你希望一个绑定函数要么只能用 new操作符,要么只能直接调用,那你必须在目标函数上显式规定这个限制。
5.快捷调用
在你想要为一个需要特定的 this 值的函数创建一个捷径(shortcut)的时候,bind() 也很好用。
使用apply:

      let arguments = 'abcde';
     // let slice = Array.prototype.slice;
     console.log(slice.apply(arguments));//["a", "b", "c", "d", "e"]

使用bind:

      let unboundSlice = Array.prototype.slice;
      let slice = Function.prototype.apply.bind(unboundSlice);
      console.log(slice(arguments));//["a", "b", "c", "d", "e"]

用 bind()可以使这个过程变得简单。上面示例中,slice 是 Function.prototype 的 apply() 方法的绑定函数,并且将 Array.prototype 的 slice() 方法作为 this 的值。这意味着我们压根儿用不着上面那个 apply()调用了。

上一篇 下一篇

猜你喜欢

热点阅读