Home >Web Front-end >JS Tutorial >JavaScript knowledge summary

JavaScript knowledge summary

小云云
小云云Original
2018-02-08 15:08:091245browse

This article mainly sorts out some similar keywords, methods, and concepts in javascript and shares them with everyone, hoping to help everyone.

1. The difference between var, function, let, and const commands

  • The scope of a variable declared using var is within the function where the statement is located, and the variable exists Promotion phenomenon

  • The scope of a variable declared using let is within the code block where the statement is located. There is no variable promotion

  • Use const What is declared is a constant, and the value and address of the constant in the stack cannot be modified in the code that appears later

  • The function declared using function has its scope where the statement is located Within the function, and there is function hoisting

  • var

    //a. 变量提升
    console.log(a) // => undefined
    var a = 123
    
    //b. 作用域
    function f() {
        var a = 123
        console.log(a) // => 123
    }
    console.log(a) // => a is not defined
    
    for (var i = 0; i < 10; i ++) {}
    console.log(i) // => 10
  • let

    //a. 变量不提升
    console.log(a) // => a is not defined
    let a = 123
    
    //b. 作用域为所在代码块内
    for (let i = 0; i < 10; i ++) {}
    console.log(i) // => i is not defined
  • const

    //a. 不能修改的是栈内存在的值和地址
    const a = 10
        a = 20 // => Assignment to constant variable 
    
    // 但是以下的赋值确是合法的
    const  a = {
        b: 20
    }
    a.b = 30
    console.log(a.b) // => 30
  • function

    //a. 函数提升
    fn() // => 123
    function fn() {
        return 123
    }
    
    //b. 作用域
    function fn() {
        function fn1 () {
            return 123456
        }
        fn1() // => 123456
    }
    fn1() // => fn1 is not defined
  • Classic interview questions

  1. var a = 1
    function fn() {
        if (!a) {
            var a = 123
        }
        console.log(a)
    }
    fn() ?
  2. // How to print out 0 - 9 in sequence

    for (var i = 0; i < 10; i++) {
        setTimeout(function(){
            console.log(i)
        })
    }
  3. function Foo() {
        getName = function(){
            console.log("1");
        };
        return this;
    }
    Foo.getName = function() {
        console.log("2");
    };
    
    Foo.prototype.getName = function(){
        console.log("3");
    };
    
    var getName = function() {
        console.log("4");
    }
    function getName(){
        console.log("5");
    }
    
    Foo.getName(); ?
    getName(); ?
    Foo().getName(); ?  
    getName(); ?
    new Foo.getName(); ?
    new Foo().getName(); ?
  • Answer:
    First question

    //我们把它执行顺序整理下
    var a = 1
    function fn() {
        var a = nudefined
        if (!a) {
            var a = 123
        }
        console.log(a)
    }
    //所以 答案很明显 就是 123

    Question 2

    for (var i = 0; i < 10; i++) {
        print(i)
    }
    function print(i) { // 把每个变量i值传进来,变成只可当前作用域访问的局部变量
        setTimeout(function(){
            console.log(i)
        })
    }
    
    // 或者自执行函数简写
    for (var i = 0; i < 10; i++) {
        (function(i){
            setTimeout(function(){
                console.log(i)
            })
        })(i)
    }

    Question 3

    // 我们整理下它的执行顺序
    var getName = undefined
    function Foo() {
        getName = function(){
            console.log("1");
        };
        return this;
    }
    function getName(){
        console.log("5");
    }
    Foo.getName = function() {
        console.log("2");
    };
    
    Foo.prototype.getName = function(){
        console.log("3");
    };
    getName = function() {
        console.log("4");
    }
    
    Foo.getName(); // 2 
    /*
    函数也是对象, Foo.getName 相当于给 Foo这个对象添加了一个静态方法 getName,我们调用的其实是这个静态方法,并不是调用的我们实例化的 getName
     */
    
    getName(); // 4  
    /*
    按照上面的执行顺序,其实这个就很好理解了,因为 `getName = function() { console.log("4"); }` 是最后一个赋值, 执行的应该是这个函数
     */
    
    Foo().getName(); // 1  
    /*
        这里为什么是 1 而不是我们想象的 3 呢?
        问题就是出在 调用的是 Foo(); 并没有使用 new 这个关键字,所以那时候返回的 this 指向的并不是 Foo, 而是 window;
        至于为什么不用 new 返回的 this 不指向 Foo, 这个随便去哪查一下就好, 就不在这介绍了
     */
    
    getName(); // 1
    /*
        这里为什么也是1 呢?  
        其实原因就是 上面我们调用了 `Foo().getName();` 这个方法引起的, 因为我们执行了 Foo 函数, 触发了
        getName = function(){
            console.log("1");
        }
        这段代码, 而且并没有在Foo里面声明  getName 变量, 于是就一直往上查找, 找到外部的 getName 变量 并赋值给它.
        所以这里调用 getName() 方法时, 它的值已经变成
        getName = function(){
            console.log("1");
        } 了
     */
    
    new Foo.getName(); // 2
    /*这个时候还是没有实例化, 调用的还是它的静态方法*/
    
    new Foo().getName(); // 3
    /*因为实例化了,所以调的是原型上的方法*/

I remember seeing a few classic examples, but I couldn’t find them after searching for a long time. That’s all for now.

2. The difference between == and ===

  • #Same points:
    Both operators are allowed Operands of any type, if the operands are equal, return true, otherwise return false

  • Differences:
    ==:The operator is called equality and is used to detect whether two operands are equal. The definition of equality here is very loose and allows type conversion.
    ===: is used to detect whether two operands are equal. Whether they are strictly equal, no type conversion will be performed

  • ##==Conversion rules

  1. First look at double equality Is there any NaN before and after the number? If there is NaN, it will always return false.

  2. Look again to see if there is a Boolean before and after the double equal sign. If there is a Boolean, convert the Boolean into a number. (false is 0, true is 1)

  3. Then check whether there is a string before and after the double equal sign. There are three situations:

    a. The other party is an object, and the object uses toString() Or valueOf() for conversion;
    b. If the other party is a number, convert the string to a number;
    c. If the other party is a string, compare directly;
    d. Otherwise, return false

  4. If it is a number and the other party is an object, the object is compared with valueOf() or toString(), otherwise it will return false

  5. null, undefined will not perform type conversion, but They are both equal

// 不同类型,相同值
var a = 1
var b = &#39;1&#39;
console.log(a == b) // => true 
console.log(a === b) // => false 

// 对象和字符串
console.log([1,2,3] == &#39;1,2,3&#39;) // => true  因为 [1,2,3]调用了 toString()方法进行转换

// 对象和布尔
console.log([] == true)  // => false  []转换为字符串&#39;&#39;,然后转换为数字0, true 转换成1

// 对象和数字
console.log([&#39;1&#39;] == 1) // => true []转换为字符串&#39;1&#39;
console.log(2 == {valueOf: function(){return 2}}) // => true  调用了 valueOf()方法进行转换

// null, undefined 不会进行类型转换,  但它们俩相等
console.log(null == 1) // => false
console.log(null == 0) // => false
console.log(undefined == 1) // => false
console.log(undefined == 0) // => false
console.log(null == false) // => false
console.log(undefined == false) // => false
console.log(null == undefined) // => true 
console.log(null === undefined) // => false

// NaN 跟任何东西都不相等(包括自己)
console.log(NaN == NaN) // => false
console.log(NaN === NaN) // => false
The following pictures show the relationship between these == ===

==
JavaScript knowledge summary

===
JavaScript knowledge summary##3. toSting and valueOf

All objects inherit these two conversion methods

toString
: Returns a string that reflects this objectvalueOf
: Returns its corresponding The original value of

    toString
  • var arr = [1,2,3]
    var obj = {
        a: 1,
        b: 2
    }
    console.log(arr.toString()) // => 1,2,3
    console.log(obj.toString()) // => [object Object]
    // 那我们修改一下它原型上的 toString 方法呢
    Array.prototype.toString = function(){ return 123 }
    Object.prototype.toString = function(){ return 456 }
    console.log(arr.toString()) // => 123
    console.log(obj.toString()) // => 456
    
    // 我们看下其余类型转换出来的结果, 基本都是转换成了字符串
    console.log((new Date).toString()) // => Mon Feb 05 2018 17:45:47 GMT+0800 (中国标准时间)
    console.log(/\d+/g.toString()) // => "/\d+/g"
    console.log((new RegExp(&#39;asdad&#39;, &#39;ig&#39;)).toString()) // => "/asdad/gi"
    console.log(true.toString()) // => "true"
    console.log(false.toString()) // => "false"
    console.log(function(){console.log(1)}.toString()) // => "function (){console.log(1)}"
    console.log(Math.random().toString()) // => "0.2609205380591437"

  • valueOf
  • var arr = [1,2,3]
    var obj = {
        a: 1,
        b: 2
    }
    console.log(arr.valueOf()) // => [1, 2, 3]
    console.log(obj.valueOf()) // => {a: 1, b: 2}
    // 证明valueOf返回的是自身的原始值
    // 同样我们修改下 valueOf 方法
    
    Array.prototype.valueOf = function(){ return 123 }
    Object.prototype.valueOf = function(){ return 456 }
    console.log(arr.valueOf()) // => 123
    console.log(obj.valueOf()) // => 456
    
    // valueOf转化出来的基本都是原始值,复杂数据类型Object返回都是本身,除了Date 返回的是时间戳
    console.log((new Date).valueOf()) // => 1517824550394  //返回的并不是字符串的世界时间了,而是时间戳
    console.log(/\d+/g.valueOf()) // => 456  当我们不设置时valueOf时,正常返回的正则表式本身:/\d+/g,只是我们设置了 Object.prototype.valueOf 所以返回的时:456
    console.log(Math.valueOf()) // => 456 同上
    console.log(function(){console.log(1)}.valueOf()) // => 456 同上

  • toString and valueOf instance
var a = {
    toString: function() {
        console.log(&#39;你调用了a的toString函数&#39;)
        return 8
    }
}
console.log( ++a) 
// 你调用了a的toString函数 
// 9  
// 当你设置了 toString 方法, 没有设置 valueOf 方法时,会调用toString方法,无视valueOf方法
  1. var a = {
        num: 10,
        toString: function() {
            console.log('你调用了a的toString函数')
            return 8
        },
        valueOf: function() {
            console.log('你调用了a的valueOf函数')
            return this.num
        }
    }
    console.log( ++a) 
    // 你调用了a的valueOf函数
    // 11
    // 而当你两者都设置了的时候,会优先取valueOf方法, 不会执行toString方法
  2. 4. The difference between || and &&

  • If "||" and "&&" are used to make conditional judgments

    • "||" As long as one of them is true, then the condition is met
    • "&&" All conditions must be true to satisfy the conditions
    • var a = true,b = false, c = true, d = false
      var str = &#39;none&#39;
      if (b || d || a) {
          str = &#39;现在是 ||&#39;
      }
      console.log(str) // => &#39;现在是 ||&#39;  ,因为其中a为true所有满足条件
      
      var str = &#39;none&#39;
      if (b || d ) {
          str = &#39;现在是 ||&#39;
      }
      console.log(str) // => &#39;none&#39; ,因为b,d都是false, 不满足条件
      
      var str = &#39;none&#39;
      if (a && c && d) {
          str = &#39;现在是 &&&#39;
      }
      console.log(str) // => &#39;none&#39; ,因为d是false, 其中有一个false就不满足条件
      
      var str = &#39;none&#39;
      if (a && c) {
          str = &#39;现在是 &&&#39;
      }
      console.log(str) // => &#39;现在是 &&&#39; ,因为b,d都是true, 满足条件

  • Short circuit principle:

    • || (or)

      : 1. As long as "||" is in front of true, the result will return the value in front of "||" 2. If "||" is false, the result will be "||" and the following value will be returned

      var a = true,b = false, c = true, d = false
      var str = &#39;none&#39;
      if (b || d || a) { str = &#39;现在是 ||&#39; }
      console.log(str) // => &#39;现在是 ||&#39;  ,因为其中a为true所有满足条件
      
      var str = &#39;none&#39;
      if (b || d ) { str = &#39;现在是 ||&#39; }
      console.log(str) // => &#39;none&#39; ,因为b,d都是false, 不满足条件
      
      var str = &#39;none&#39;
      if (a && c && d) { str = &#39;现在是 &&&#39; }
      console.log(str) // => &#39;none&#39; ,因为d是false, 其中有一个false就不满足条件
      
      var str = &#39;none&#39;
      if (a && c) { str = &#39;现在是 &&&#39; }
      console.log(str) // => &#39;现在是 &&&#39; ,因为b,d都是true, 满足条件

    • && (with)

      1. As long as "&&" is in front of false, no matter "&&" is followed by true or false, the result will return the value in front of "&&" 2. As long as "&&" is in front of true, no matter "&&" is followed by true or false, the result will return the value after "&&"

      var a = false, b = true
      console.log(a && b) // => false  只要“&&”前面是false,无论“&&”后面是true还是false,结果都将返“&&”前面的值
      console.log(b && a) // => false  只要“&&”前面是true,无论“&&”后面是true还是false,结果都将返“&&”后面的值

    5. The difference between call/bind/apply
var name = &#39;小刚&#39;
var person = {
    name: &#39;小明&#39;,
    fn: function() {
        console.log(this.name + &#39;撸代码&#39;)
    }
}
person.fn() // => 小明撸代码
// 如何把它变成  “小刚撸代码”  呢?

// 我们可以用 call/bind/apply 分别来实现
person.fn.call(window) // => 小刚撸代码
person.fn.apply(window) // => 小刚撸代码
person.fn.bind(window)() // => 小刚撸代码

Obviously, call and apply More similar, bind is different from the two forms

What is the difference between call and apply?

obj.call(thisObj, arg1, arg2, ...)
obj.apply(thisObj, [arg1, arg2, ...])
// 通过上面的参数我们可以看出, 它们之间的区别是apply接受的是数组参数,call接受的是连续参数。
// 于是我们修改上面的函数来验证它们的区别

var person = {
    name: &#39;小明&#39;,
    fn: function(a,b) {
        if ({}.toString.call(a).slice(8, -1) === &#39;Array&#39;) {
            console.log(this.name+&#39;,&#39;+a.toString()+&#39;撸代码&#39;)
        }else{
            console.log(this.name+&#39;,&#39;+a+&#39;,&#39;+b+&#39;撸代码&#39;)
        } 
    }
}

person.fn.call(this, &#39;小红&#39;, &#39;小黑&#39; ) // => 小刚,小红,小黑撸代码
person.fn.apply(this, [&#39;小李&#39;, &#39;小谢&#39;]) // => 小刚,小李,小谢撸代码

So what is the difference between bind and call,apply?

The difference between call and apply is , bind will not be executed immediately after binding. It will only determine the this point of the function and then return the function

var name = "小红"
var obj = {
    name: &#39;小明&#39;,
    fn: function(){
        console.log(&#39;我是&#39;+this.name)
    }
}
setTimeout(obj.fn, 1000) // => 我是小红
// 我们可以用bind方法打印出 "我是小明"
setTimeout(obj.fn.bind(obj), 1000) // => 我是小明
// 这个地方就不能用 call 或 apply 了, 不然我们把函数刚一方去就执行了

// 注意: bind()函数是在 ECMA-262 第五版才被加入
// 所以 你想兼容低版本的话 ,得需要自己实现 bind 函数
Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(
              this instanceof fNOP && oThis ? this : oThis || window,
              aArgs.concat(Array.prototype.slice.call(arguments))
          );
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
};

6. callback 、 promise 、 async/await

这三个东西牵涉到的可能就是我们最常见到的 “同步”、“异步”、“任务队列”、“事件循环”  这几个概念了


    • 例:

      var data;
      $.ajax({
          ...
          success: function(data) {
              data = data
          }
      })
      console.log(data)

      当我们从服务器获取到数据的时候,为什么打印出来的是undefined ?
      解决这个问题之前我们先来了解javascript的运行环境

      JavaScript是单线程语言,JS中所有的任务可以分为两种:同步任务和异步任务。

    • 同步任务:
      意思是我必须做完第一件事,才能做第二件事,按照顺序一件一件往下执行(在主线程上)

    • 异步任务:
      假如我第一件事需要花费 10s, 但是我第二件事急着要做, 于是我们就把第一件事告诉主线程,然后主线程暂停先放到某个地方, 等把第二件事完成之后,再去那个地方执行第一件事,第一件事也就可以理解为异步任务

    • 任务队列(task queue):
      任务队列是干嘛的呢; 上面我们说了异步任务的情况,  我们把第一件放到某个地方, 那某个地方是什么地方呢,就是 “任务队列” 这个东西。里面乘放的是所有异步任务。

    • Event Loop(事件循环)
      当主线程上面所有同步任务执行完之后,主线程就会向任务队列中读取异步任务(队列方法:先进先出)
      而且是一直重复向任务队列中,即使没有任务。它也会一直去轮询。
      只不过在任务列表里面没有任务的时候, 主线程只需要稍微过一遍就行, 一旦遇到任务队列里面有任务的时候,就会去执行它
      也就是说在我们打开网页的时候,JS引擎会一直执行事件循环,直到网页关闭

      如图所示
      JavaScript knowledge summary

      由此,上面为什么会产生 undefined的原因了, 因为ajax 是异步任务,而我们console.log(data)是同步任务,所以先执行的同步任务,才会去执行 ajax

      说了这么多,我们来看下 为什么我们很需要 从 callback => promise => async/await

      因为很多时候我们需要把一个异步任务的返回值,传递给下一个函数,而且有时候是连续的n个

  1. callback

    // 只有一个callback的时候
    function fn(callback) {
        setTimeout(function(){
            callback && callback()
        }, 1000)
    }
    fn(function(){
        console.log(1)
    })
    
    // 一旦我们多几个呢?
    function fn(a){ // 传入a  返回a1
        function fn1(a1){
            function fn2(a2){
                function fn3(a3){
                    console.log(a3)
                    ....
                }
            }
        }
    }
    // 当项目一复杂,这滋味。。。
  2. Promise

  • 什么是promise?
    Promise是异步编程的一种解决方案,同时也是ES6的内置对象,它有三种状态:

  • Promise方法

  • 基本用法

    let promise = new Promise( (resolve, reject) => {
        setTimeout(function(){
            resolve(1)
        }, 1000)
    })
    promise.then( res => {
        console.log(res)// 一秒之后打印1
    })
  • 我们把上面的回调地狱转换下

    const fn = a => {
        return Promise.resolve(a)
    }
    const fn1 = a => {
        return Promise.resolve(a)
    }
    const fn2 = a => {
        // return Promise.resolve(a)
        return new Promise( (resolve, reject) => {
            setTimeout(function(){
                resolve(a)
            },1000)
        })
    }
    const fn3 = a => {
        // return Promise.resolve(a)
        return new Promise( (resolve, reject) => {
            setTimeout(function(){
                resolve(a)
            },1000)
        })
    }
    fn(123)
        .then(fn1)
        .then(fn2)
        .then(fn3)
        .then( res => {
            console.log(res) // => 123
        })

    这样就简单明了多了, 我们就不需要一层一层嵌套callback了,可以通过链式调用来解决callback的问题

    然而,仅仅这样还是觉得不够好
    因为这种面条式调用还是让人很不爽,而且 then 方法里面虽然是按先后顺序来的,但是其本身还是异步的
    看下面这段代码

    const promise = new Promise( (resolve, reject) => {
        setTimeout(function(){
            resolve(222)
        }, 1000)
    })
    console.log(111)
    promise.then( res => {
        console.log(res)
    })
    console.log(333)

    打印结果依然还是 111 => 333 => 222, 并不是我们想象的 111 => 222 => 333
    依然不适合单线程的思维模式。所以下一个解决方案 又出现了

  1. Promise.prototype.then()  接收两个函数,一个是处理成功后的函数,一个是处理错误结果的函数。可以进行链式调用

  2. Promise.prototype.catch() 捕获异步操作时出现的异常, 一般我们用来代替.then方法的第二个参数

  3. Promise.resolve()  接受一个参数值,可以是普通的值, 会返回到对应的Promise的then方法上

  4. Promise.reject()  接受一个参数值,可以是普通的值, 会返回到对应的Promise的catch方法上或着then方法的第二个参数上

  5. Promise.all() 接收一个参数,它必须是可以迭代的,比如数组。通常用来处理一些并发的异步操作。成功调用后返回一个数组,数组的值是有序的,即按照传入参数的数组的值操作后返回的结果

  6. Promise.race() 接收一个可以迭代的参数,比如数组。但是只要其中有一个执行了,就算执行完了,不管是成功还是失败。

  7. pending: 进行中

  8. resolved: 已完成

  9. rejected:已失败

  • async/await
    这是ES7的语法,当然,在现在这种工程化的时代,基本babel编译之后也都是能在项目中引用的

    • 基本用法跟规则
      async 表示这是一个async函数,
      await只能用在这个函数里面。后面应该跟着是 Promise 对象, 不跟的话也没关系, 但是await就不会在这里等待了
      await 表示在这里等待promise返回结果

      例:

      const fn = () => {
          return new Promise( (resolve, reject) => {
              setTimeout(function(){
                  resolve(222)
              }, 1000)
          })
      }
      (async function(){
          console.log(111)
          let data = await fn()
          console.log(data)
          console.log(333)
      })()
      // 是不是返回 111 => 222 => 333 了呢
      
      // 我们来试下返回别的东西, 不返回 promise
      const fn = () => {
          return new Promise( (resolve, reject) => {
              setTimeout(function(){
                  resolve(222)
              }, 1000)
          })
      }
      (async function(){
          console.log(111)
          let data = await fn()
          console.log(data)
          console.log(333)
      })()
      // 打印结果: 111 => null => 333 => 222
      // 当我们不是在await 关键字后面返回的不是 promise 对象时, 它就不会在原地等待 promise执行完再执行, 而是向正常的JS一样执行,把异步任务跳过去
    • await 关键字必须包裹在 async 函数里面,而且async 函数必须是它的父函数

      const fn = () => {
          let promise = new Promise( (resolve, reject) => {
              setTimeout(function(){
                  resolve(222)
              }, 1000)
          })
      }
      
      // 这样是不行的,会报错,因为的await关键字的父函数不是 async 函数
      const grand = async () => {
          return function parent() {
              let data = await fn()
          }
      }
      
      // 这样才行,因为await 的父函数 是一个 async 函数
      const grand = () => {
          return async function parent() {
              let data = await fn()
          }
      }

    7. 柯里化 与 反柯里化

    • 柯里化
       函数柯里化就是对高阶函数的降阶处理。
       柯里化简单的说,就是把 n 个参数的函数,变成只接受一个参数的 n 个函数
      function(arg1,arg2)变成function(arg1)(arg2)
      function(arg1,arg2,arg3)变成function(arg1)(arg2)(arg3)
      function(arg1,arg2,arg3,arg4)变成function(arg1)(arg2)(arg3)(arg4)

      • 柯里化有什么作用

      • 例:

        //求和
        function add (a, b, c) {
            return a + b + c
        }
        add(1,2,3)

        如果我只改变 c 的值,在求和
        add(1,2,4) 是不是得多出重新计算  a + b 的部分
         我们是不是可以提前返回a+b的值, 然后只传入 c 的值进行计算就行了
         修改一下方法

        function add (a, b) {
            return function (c) {
                return a + b + c
            }
        }
        var sum = add(1, 2)
        sum(3)
        sum(4)

        在此基础上我们在做下修改

        function add (a) {
            return function (b) {
                return function (c) {
                    return a + b + c
                }
            }
        }

        这样我们是不是可以随时复用某个参数,并且控制在某个阶段提前返回

        还有一个经典的例子

        var addEvent = function(el, type, fn, capture) {
            if (window.addEventListener) {
                el.addEventListener(type, function(e) {
                    fn.call(el, e);
                }, capture);
            } else if (window.attachEvent) {
                el.attachEvent("on" + type, function(e) {
                    fn.call(el, e);
                });
            } 
        };

        我们每次调用事件时,都需要判断兼容问题, 但我们运用柯里化的方式就只要判断一次就行了

        var addEvent = (function(){
            if (window.addEventListener) {
                return function(el, sType, fn, capture) {
                    el.addEventListener(sType, function(e) {
                        fn.call(el, e);
                    }, (capture));
                };
            } else if (window.attachEvent) {
                return function(el, sType, fn, capture) {
                    el.attachEvent("on" + sType, function(e) {
                        fn.call(el, e);
                    });
                };
            }
        })();

        还有一个作用就是延迟计算

        小明每天都会花一部分钱吃饭
         小明想知道它5天之后总共会花费多少钱

        var total = 0
        var fn = function(num) {
            total += num
        }
        fn(50)
        fn(70)
        fn(60)
        fn(100)
        fn(80)

        这样我们便能算出它总共花了都少钱

        但是小明又突然想知道 如果他每天花费的的钱翻一倍 会产生多少钱
         于是我们是不是得改下 上面的 函数

        var fn = function(num) {
            total += num*2
        }
        fn(50)
        fn(70)
        fn(60)
        fn(100)
        fn(80)

        那我们是不是有什么办法,先把这些数 存起来,到最后在进行计算
         我们接着来封装

        var curry = function(fn) {
            var args = []
            return function() {
                if (arguments.length === 0) {
                    return fn.apply(null, args)
                }else{
                    args = args.concat([].slice.call(arguments))
                    return curry.call(null, fn, args)
                }
            }
        }
        
        var curryFn = function() {
            var args = [].slice.call(arguments),
                total = 0
            for (var i = 0; i 9dbef05f8660b044d4e176bb951d5597 args.length) {
                    var newArgs = [fn].concat(args);
                    return curry(sub_curry.apply(null,newArgs), length - args.length)
                }else{
                    fn.apply(null,arguments)
                }
            }
        }
        // 1.
        var fn  = curry( function(a,b,c){
            console.log(a, b, c)
        })
        fn('a')('b')('c')
        
        // 2.
        fn1 = curry(function(){
            console.log(arguments)
        }, 3)
        fn1('a')('b')('c')
    1. 参数复用;

    2. 提前返回;

    3. 延迟计算/运行

  • 反柯里化
     反柯里化的作用在与扩大函数的适用性,使本来作为特定对象所拥有的功能的函数可以被任意对象所用.

    被任意对象使用? 是不是想到了用call, apply 设置this指向

    • 通过 call/apply  被任意对象所用

      var obj = {
          a: 1,
          fn: function (b) {
              return this.a + b
          }
      }
      obj.fn(2) // 3
      var obj1 = {a:4}
      obj.fn.call(obj1, 2) // 6
    • 反柯里化版本

      var uncurrying= function (fn) {
          return function () {
              var context=[].shift.call(arguments);
              return fn.apply(context,arguments);
          }
      }
      // const uncurrying = fn => (...args) => Function.prototype.call.apply(fn,args) // 简洁版
      var f = function (b) {
          return this.a + b
      }
      var uncurry = uncurrying(f)
      var obj = {a:1},
          obj1 = {a:4}
      uncurry(obj, 2) // 3
      uncurry(obj1, 2) // 3

    相信大家已经看出区别了,这丫的就相当于一个外部的call方法

    总结

    上面很多只是自己的部分理解,不一定准确。如果有不同理解,谢谢指出。

    相关推荐:

    最全JavaScript知识点总结

    一些容易犯错的JavaScript知识点整理

    JavaScript知识点系统总结

    The above is the detailed content of JavaScript knowledge summary. For more information, please follow other related articles on the PHP Chinese website!

    Statement:
    The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn