JavaScriptの知識まとめ

小云云
リリース: 2018-02-08 15:08:09
オリジナル
1173 人が閲覧しました

この記事は、主にjavascriptの類似したキーワード、メソッド、概念をいくつか整理し、皆さんと共有し、皆さんのお役に立てれば幸いです。javascript中一些相似的关键字、方法、概念,并分享给大家,希望能帮助到大家。

1. var、function、let、const 命令的区别

  • 使用var声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象

  • 使用let声明的变量,其作用域为该语句所在的代码块内,不存在变量提升

  • 使用const声明的是常量,在后面出现的代码中不能再修改该常量的栈内存在的值和地址

  • 使用function声明的函数,其作用域为该语句所在的函数内,且存在函数提升现象

  • 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
    ログイン後にコピー
  • 经典面试题

  1. var a = 1 function fn() { if (!a) { var a = 123 } console.log(a) } fn() ?
    ログイン後にコピー
  2. // 如何依次打印出0 - 9

    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(); ?
    ログイン後にコピー
  • 答案:
    第一题

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

    第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) }
    ログイン後にコピー

    第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 /*因为实例化了,所以调的是原型上的方法*/
    ログイン後にコピー

我记得看到过几个经典的例子,找了半天没找到, 暂时就这些吧.。

2. == 与 === 的区别

  • 相同点:
    它们两个运算符都允许任意类型的的操作数,如果操作数相等,返回true,否则返回false

  • 不同点:
    ==:运算符称作相等,用来检测两个操作数是否相等,这里的相等定义的非常宽松,可以允许进行类型转换
    ===:用来检测两个操作数是否严格相等,不会进行类型转换

  • == 转换规则

    1. 首先看双等号前后有没有NaN,如果存在NaN,一律返回false。

    2. 再看双等号前后有没有布尔,有布尔就将布尔转换为数字。(false是0,true是1)

    3. 接着看双等号前后有没有字符串, 有三种情况:
      a. 对方是对象,对象使用toString()或者valueOf()进行转换;
      b. 对方是数字,字符串转数字;
      c. 对方是字符串,直接比较;
      d. 其他返回false

    4. 如果是数字,对方是对象,对象取valueOf()或者toString()进行比较, 其他一律返回false

    5. null, undefined不会进行类型转换, 但它们俩相等

// 不同类型,相同值 var a = 1 var b = '1' console.log(a == b) // => true console.log(a === b) // => false // 对象和字符串 console.log([1,2,3] == '1,2,3') // => true 因为 [1,2,3]调用了 toString()方法进行转换 // 对象和布尔 console.log([] == true) // => false []转换为字符串'',然后转换为数字0, true 转换成1 // 对象和数字 console.log(['1'] == 1) // => true []转换为字符串'1' 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
ログイン後にコピー

下面几张图表示这些 == === 的关系

==
JavaScriptの知識まとめ

===
JavaScriptの知識まとめ

3. toSting 和 valueOf

所有对象继承了这两个转换方法
toString: 返回一个反映这个对象的字符串
valueOf

1. var、function、let、constコマンドの違い
  • varを使用して宣言された変数は、ステートメントが配置されている関数内にスコープがあり、変数の昇格現象が発生します
  • let を使用して宣言されており、そのスコープはステートメントが配置されているコード ブロック内にあります。変数昇格はありません。定数は const を使用して宣言されています。後で出てきます
  • functionで宣言した関数のスコープがその文が置かれている関数内にあり、関数巻き上げ現象が発生します
  1. var
  2. 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('asdad', 'ig')).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"
    ログイン後にコピー
let

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 同上
ログイン後にコピー

  • const

    var a = { toString: function() { console.log('你调用了a的toString函数') return 8 } } console.log( ++a) // 你调用了a的toString函数 // 9 // 当你设置了 toString 方法, 没有设置 valueOf 方法时,会调用toString方法,无视valueOf方法
    ログイン後にコピー

    関数
  • 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方法
    ログイン後にコピー
    • 典型的な面接の質問

    • var a = true,b = false, c = true, d = false var str = 'none' if (b || d || a) { str = '现在是 ||' } console.log(str) // => '现在是 ||' ,因为其中a为true所有满足条件 var str = 'none' if (b || d ) { str = '现在是 ||' } console.log(str) // => 'none' ,因为b,d都是false, 不满足条件 var str = 'none' if (a && c && d) { str = '现在是 &&' } console.log(str) // => 'none' ,因为d是false, 其中有一个false就不满足条件 var str = 'none' if (a && c) { str = '现在是 &&' } console.log(str) // => '现在是 &&' ,因为b,d都是true, 满足条件
      ログイン後にコピー
  • // 0 - 9

    var a = true,b = false, c = true, d = false var str = 'none' if (b || d || a) { str = '现在是 ||' } console.log(str) // => '现在是 ||' ,因为其中a为true所有满足条件 var str = 'none' if (b || d ) { str = '现在是 ||' } console.log(str) // => 'none' ,因为b,d都是false, 不满足条件 var str = 'none' if (a && c && d) { str = '现在是 &&' } console.log(str) // => 'none' ,因为d是false, 其中有一个false就不满足条件 var str = 'none' if (a && c) { str = '现在是 &&' } console.log(str) // => '现在是 &&' ,因为b,d都是true, 满足条件
    ログイン後にコピー
    var a = false, b = true console.log(a && b) // => false 只要“&&”前面是false,无论“&&”后面是true还是false,结果都将返“&&”前面的值 console.log(b && a) // => false 只要“&&”前面是true,无论“&&”后面是true还是false,结果都将返“&&”后面的值
    ログイン後にコピー

    • の答えを印刷する方法:最初の質問

      var name = '小刚' var person = { name: '小明', fn: function() { console.log(this.name + '撸代码') } } person.fn() // => 小明撸代码 // 如何把它变成 “小刚撸代码” 呢? // 我们可以用 call/bind/apply 分别来实现 person.fn.call(window) // => 小刚撸代码 person.fn.apply(window) // => 小刚撸代码 person.fn.bind(window)() // => 小刚撸代码
      ログイン後にコピー

      質問 2
      obj.call(thisObj, arg1, arg2, ...) obj.apply(thisObj, [arg1, arg2, ...]) // 通过上面的参数我们可以看出, 它们之间的区别是apply接受的是数组参数,call接受的是连续参数。 // 于是我们修改上面的函数来验证它们的区别 var person = { name: '小明', fn: function(a,b) { if ({}.toString.call(a).slice(8, -1) === 'Array') { console.log(this.name+','+a.toString()+'撸代码') }else{ console.log(this.name+','+a+','+b+'撸代码') } } } person.fn.call(this, '小红', '小黑' ) // => 小刚,小红,小黑撸代码 person.fn.apply(this, ['小李', '小谢']) // => 小刚,小李,小谢撸代码
      ログイン後にコピー

      質問 3
    • var name = "小红" var obj = { name: '小明', fn: function(){ console.log('我是'+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; };
      ログイン後にコピー
      ログイン後にコピー
    • いくつかの古典的な例を見た覚えがありますが、検索しても見つかりませんでした。長くなりましたので、今回はここまでです。

      2. == と === の違い

同じ点:


どちらの演算子も、オペランドが等しい場合は true を返し、それ以外の場合は false を返します


相違点。 :

==: 演算子は等価と呼ばれ、2 つのオペランドが等しいかどうかを検出するために使用されます。ここでの等価の定義は非常に緩やかで、型変換が可能です。 ===: 2 つのオペランドが等しいかどうかを検出するために使用します。は厳密に等しいため、型変換は実行されません == 変換ルール まず、二重等号の前後に NaN があるかどうかを確認し、NaN がある場合は常に false を返します。 二重等号の前後にブール値があるかどうかを確認します。ブール値がある場合は、ブール値を数値に変換します。 (false が 0、true が 1) 次に、二重等号の前後に文字列があるかどうかを確認します。 a. 相手がオブジェクトであり、そのオブジェクトが toString を使用して変換されます。 () または valueOf(); b. 相手が数値の場合、文字列を数値に変換します d. 相手が文字列の場合、 false を返します。は数値、相手はオブジェクトで、オブジェクトは比較にvalueOf()またはtoString()を使用します。それ以外はfalsenullを返します、unknownは型変換を行いませんが、それらは等しいです
var data; $.ajax({ ... success: function(data) { data = data } }) console.log(data)
ログイン後にコピー
ログイン後にコピー
次の写真はこれらの関係を示しています == =====JavaScriptの知識まとめ===JavaScriptの知識まとめ3. toSting と valueOfすべてのオブジェクトはこれら 2 つの変換メソッドを継承しますtoString: このオブジェクトを反映する文字列を返しますvalueOf: 対応する toString
// 只有一个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) .... } } } } // 当项目一复杂,这滋味。。。
ログイン後にコピー
ログイン後にコピー
valueOf
let promise = new Promise( (resolve, reject) => { setTimeout(function(){ resolve(1) }, 1000) }) promise.then( res => { console.log(res)// 一秒之后打印1 })
ログイン後にコピー
ログイン後にコピー
toString と valueOf インスタンス の元の値を返します。
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 })
ログイン後にコピー
ログイン後にコピー
4. || & の違い 条件判断に使用される場合"そのうちの 1 つが true であれば、条件が満たされます"&&" 条件を満たすには、すべての条件が true でなければなりません
const promise = new Promise( (resolve, reject) => { setTimeout(function(){ resolve(222) }, 1000) }) console.log(111) promise.then( res => { console.log(res) }) console.log(333)
ログイン後にコピー
ログイン後にコピー
短絡原理: || : 1. 「||」が前にある限り、結果は「||」の前の値を返します。 2. 「||」が前にある場合、結果は「 ||" と次の値が返されます
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一样执行,把异步任务跳过去
ログイン後にコピー
ログイン後にコピー
&& (and): 1. "&&" が false の前にある限り、"&&" の後に true または false が続くかどうかに関係なく、結果は「&&」より前の値を返します 2.「&&」より前の値がtrueであれば、「&&」以降の値がtrueかfalseかに関わらず、結果は「&&」より後の値を返します
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() } }
ログイン後にコピー
ログイン後にコピー
5. call /bind/apply の違い
//求和 function add (a, b, c) { return a + b + c } add(1,2,3)
ログイン後にコピー
ログイン後にコピー
明らかに、call と apply は似ていますが、bind は形式が異なります それでは、call と apply の違いは何ですか?
function add (a, b) { return function (c) { return a + b + c } } var sum = add(1, 2) sum(3) sum(4)
ログイン後にコピー
ログイン後にコピー
それでは、 binding と call,apply の違い と call と apply の違いは、bind がバインド直後に実行されないことです。関数のこの点のみを決定して、関数を返します
var name = "小红" var obj = { name: '小明', fn: function(){ console.log('我是'+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の知識まとめ

      由此,上面为什么会产生 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:已失败

    3. 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 < args.length; i++) { total += args[i] } return total } var fn = curry(curryFn) fn(50) fn(70) fn(60) fn(100) fn(80) fn() //不传参数的时候进行计算
        ログイン後にコピー

        这样我们只有最后的时候才进行计算。
        而且只需要修改 curryFn 里面的计算方法就行

        我们整理下上面的方法封装完整的柯里化函数

        var curry = function (fn, length) { length = length || fn.length; var sub_curry = function (f) { var args = [].slice.call(arguments, 1); return function () { return f.apply(null, args.concat([].slice.call(arguments))) } } return function () { var args = [].slice.call(arguments); if (length > 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知识点系统总结

      以上がJavaScriptの知識まとめの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!