前端總結--javascript

1. JavaScript的數據類型

clipboard.png

1.1 JavaScript有幾種類型的值

  1. 基本類型(值類型)javascript

    • 字符串(String)
    • 數字(Number)
    • 布爾(Boolean)
    • 空(Null)
    • 未定義(Undefined)
    • 獨一無二的值(Symbol)
  2. 引用類型html

    • 對象(Object)
    • 數組(Array)
    • 函數(Function)
    • ...

1.2 基本數據類型

1.2.1 值是不可變的

var name = 'jie';
        name.toUpperCase();
        console.log(name); //jie

1.2.2 存放在棧區

基本數據類型直接存儲在棧(stack)中的簡單數據段java

爲何放入棧中存儲
  1. 佔據空間小
  2. 大小固定
  3. 屬於被頻繁使用數據

1.2.3 值的比較

var a = 1;
        var b = true;
        console.log(a == b)  //true
        console.log(a === b); //false
  1. == : 只進行值的比較,會進行數據類型的轉換。
  2. === : 不只進行值得比較,還要進行數據類型的比較。

1.3 引用數據類型

1.3.1 值是可變的

var a = {
            age: 20
        }
        var b = a;
        b.age = 21;
        console.log(a.age)  //21
        console.log(b.age)  //21
        console.log(a === b) //true

1.3.2 同時保存在棧內存和堆內存

  1. 引用數據類型存儲在堆(heap)中的對象,佔據空間大、大小不固定,若是存儲在棧中,將會影響程序運行的性能;
  2. 引用數據類型在棧中存儲了指針,該指針指向堆中該實體的起始地址
  3. 當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中得到實體。

clipboard.png

1.3.3比較是引用的比較

var a = {
            age: 20
        }
        var b = a;
        b.age = 21;
        console.log(a.age) //21
        console.log(b.age) //21

變量a初始化時,a指針指向對象{age:20}的地址,a賦值給b後,b又指向該對象{age:20}的地址,這兩個變量指向了同一個對象。所以,改變其中任何一個變量,都會相互影響。node

clipboard.png

若是取消某一個變量對於原對象的引用,不會影響到另外一個變量git

var a = {
            age: 20
        }
        var b = a;
        a = 1;
        console.log(a)  //1
        console.log(b)  //{age:20}

a和b指向同一個對象,而後a的值變爲1,這時不會對b產生影響,b仍是指向原來的那個對象。es6

1.3.4 一道面試題

function test(person) {
  person.age = 26
  person = {
    name: 'hzj',
    age: 18
  }
  return person
}
const p1 = {
  name: 'fyq',
  age: 19
}
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?
p1:{name: 「fyq」, age: 26}
p2:{name: 「hzj」, age: 18}

2.JavaScript檢測

2.1 typeof(類型)

  1. typeof返回一個表示數據類型的字符串,返回結果包括:number、boolean、string、symbol、object、undefined、function等7種數據類型,
  2. 但不能判斷null、array
typeof Symbol(); // symbol 有效
typeof ''; // string 有效
typeof 1; // number 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof new Function(); // function 有效
typeof null; //object 無效
typeof [] ; //object 無效
typeof new Date(); //object 無效
typeof new RegExp(); //object 無效

2.2 instanceof(實例)

  1. instanceof 是用來判斷A是否爲B的實例,表達式爲:A instanceof B,若是A是B的實例,則返回true,不然返回false。
  2. instanceof 運算符用來測試一個對象在其原型鏈中是否存在一個構造函數的 prototype 屬性。
  3. 不能檢測null 和 undefined
[] instanceof Array; //true
{} instanceof Object;//true
new Date() instanceof Date;//true
new RegExp() instanceof RegExp//true

2.3 constructor(構造函數,建設者)

null 和 undefined 是無效的對象,所以是不會有 constructor 存在的,這兩種類型的數據須要經過其餘方式來判斷github

2.4Object.prototype.toString.call()

Object.prototype.toString.call('') ;   // [object String]
Object.prototype.toString.call(1) ;    // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window是全局對象global的引用

2.5 參考

https://github.com/ljianshu/B...面試

3. 函數(方法)

3.1 生成函數的三種方式

3.1.1 函數聲明

function fn(){
            console.log('aa')
        }
        fn()

3.1.2 函數表達式

var fn = function() {
            console.log('aa')
        }
        fn()

3.1.3 當即執行函數

var i = 'aa';
        (function(i) {
            console.log(i)  //aa
        })(i)
function fn(i) {
            (function() {
                console.log(i) //aa
            })()
        }
        fn('aa')
function fn() {
            (function(i) {
                console.log(i)   // undefined
            })()
        }
        fn('aa')
function fn() {
            (function() {
                console.log(i)  
            })(i)
        }
        fn('aa')

clipboard.png

3.2 函數的做用域

函數的做用域分爲全局做用ajax

3.2.1先聲明變量,再調用方法

var a = 'aa'
        fn()

        function fn() {
            var b = 'bb'
            c = 'cc'
            console.log(a)    //aa
            console.log(b)    //bb
            console.log(c)    //cc
        }
        console.log(a)    //a
        console.log(c)    //cc
        console.log(b)    //b is not defined

3.2.先調用方法,再聲明變量

fn()
        var a = 'aa'
        function fn() {
            var b = 'bb'
            c = 'cc'
            console.log(a)    //aa
            console.log(b)    //bb
            console.log(c)    //cc
        }
        console.log(a)    //undefined
        console.log(b)    //b is not defined
        console.log(c)    //cc

3.3 變量生命週期

JavaScript 變量生命週期在它聲明時初始化。json

  1. 局部變量在函數執行完畢後銷燬。
  2. 全局變量在頁面關閉後銷燬。

4. 原型到原型鏈

爲何會存在

由於js要實現繼承,js沒有像別的語言有繼承這個東西(es6中的class本質上也是基於原型和原型鏈),

4.1名詞解釋

  1. constructor 構造函數
  2. prototype 原型(顯式原型),只有函數纔有 prototype 屬性
  3. __proto__ 原型鏈(隱式原型),每個JavaScript對象(除了 null )都具備的一個屬性,函數也是對象,因此函數也有__proto__

4.2 構造函數建立對象

4.2.1最簡單的構造函數

  1. 函數名字爲大寫
  2. 與new配合
  3. Person 就是一個構造函數,咱們使用 new 建立了一個實例對象 person
function Person() {

        }
        var person = new Person();
        person.name = 'jie'
        console.log(person.name)

4.2.2 prototype(原型)

那什麼是原型呢

每個JavaScript對象(null除外)在建立的時候就會與之關聯另外一個對象,這個對象就是咱們所說的原型,每個對象都會從原型"繼承"屬性

  1. 每一個函數都有一個 prototype 屬性
  2. 函數的 prototype 屬性指向了一個對象,這個對象正是調用該構造函數而建立的實例的原型,也就是這個例子中的 person1 和 person2 的原型
function Person() {

        }
        Person.prototype.name = 'jie'
        var person1 = new Person(); 
        var person2 = new Person(); 
        console.log(person1.name)    //jie
        console.log(person2.name)    //jie
用一張圖表示構造函數和實例原型之間

clipboard.png

4.2.3 proto

  1. 每個JavaScript對象(除了 null )都具備的一個屬性,叫__proto__,
  2. 這個屬性會指向該對象的原型
function Person() {

        }
        var person = new Person();
        console.log(person.__proto__ === Person.prototype) //true
用一張圖表示實例和實例原型之間的__proto__

clipboard.png

4.2.4 constructor

每一個原型都有一個 constructor 屬性指向關聯的構造函數

function Person() {

        }
        console.log(Person === Person.prototype.constructor) //true

clipboard.png

4.3 實例與原型

  1. 當讀取實例的屬性時,若是找不到,就會查找與對象關聯的原型中的屬性
  2. 若是還查不到,就去找原型的原型,一直找到最頂層爲止
function Person() {

        }
        Person.prototype.name = '原型上的名字';
        var person1 = new Person();
        var person2 = new Person();
        person1.name = '實例名字1';
        person2.name = '實例名字2';
        console.log('person1.name:' + person1.name) //實例名字1
        console.log('person2.name:' + person2.name) //實例名字2

        delete person1.name;
        console.log('person1.name:' + person1.name) //原型上的名字
        console.log('person2.name:' + person2.name) //實例名字1

4.4 原型的原型

原型也是一個對象,既然是對象,咱們就能夠用最原始的方式建立它

var obj = new Object();
        obj.name = 'jie'
        console.log(obj.name)

clipboard.png

4.5 原型鏈

function Person() {

        }
        var p1 = new Person();
        console.log(p1.__proto__ === Person.prototype)  //true
        console.log(Person.prototype.__proto__ === Object.prototype)    //true
        console.log(Object.prototype.__proto__ === null)    //true

圖中由相互關聯的原型組成的鏈狀結構就是原型鏈,也就是藍色的這條線。

clipboard.png

4.6 其它

4.6.1 person.constructor

function Person() {

}
var person = new Person();
console.log(person.constructor === Person); // true

當獲取 person.constructor 時,其實 person 中並無 constructor 屬性,當不能讀取到constructor 屬性時,會從 person 的原型也就是 Person.prototype 中讀取,正好原型中有該屬性,因此:

person.constructor === Person.prototype.constructor

4.6.2 真的是繼承嗎

繼承意味着複製操做,然而 JavaScript 默認並不會複製對象的屬性,相反,JavaScript 只是在兩個對象之間建立一個關聯,這樣,一個對象就能夠經過委託訪問另外一個對象的屬性和函數,因此與其叫繼承,委託的說法反而更準確些

4.7 參考

https://github.com/mqyqingfen...

5.詞法做用域

JavaScript 採用詞法做用域(lexical scoping),也就是靜態做用域

var value = 1;

        function fn1() {
            console.log(value)
        }

        function fn2() {
            var value = 2;
            fn1()
        }
        fn2()

按照函數棧先進後出的順序執行,先執行完fn1(),再執行完fn2()

執行 fn1 函數,先從 fn1 函數內部查找是否有局部變量 value,若是沒有,就根據書寫的位置(),查找上面一層的代碼,也就是 value 等於 1,因此結果會打印 1

6.執行上下文

6.1 聲明(建立) JavaScript 變量

使用 var 關鍵詞來聲明變量

var carname;

變量聲明以後,該變量是空的(它沒有值)。
如需向變量賦值,請使用等號:

carname="Volvo";

不過,您也能夠在聲明變量時對其賦值:

var carname="Volvo";

6.2 變量聲明的各類狀況

6.2.1 打印未聲明和未賦值

console.log(a)

clipboard.png

6.2.2 先打印,再聲明和未賦值

console.log(a)    //undefined
        var a;

6.2.3 先打印,再聲明和未賦值

console.log(a)    //undefined
        var a = 10;

6.2.4 聲明和未賦值,再打印,

var a = 10;
        console.log(a)

6.3 函數聲明的各類狀況

 6.3.1 函數聲明

console.log(f1)

        function f1() {

        }

clipboard.png

6.3.2 函數表達式

console.log(f2)
        var f2 = function() {

        }

clipboard.png

6.4 this

console.log(this)

clipboard.png

6.5 什麼是執行上下文

執行上下文也叫作執行上下文環境
給執行上下文環境下一個通俗的定義:在執行代碼以前,把將要用到的全部的變量都事先拿出來,有的直接賦值了,有的先用undefined佔個空

  1. 變量、函數表達式——變量聲明,默認賦值爲undefined;
  2. this——賦值;
  3. 函數聲明——賦值;

這三種數據的準備狀況咱們稱之爲「執行上下文」或者「執行上下文環境」

6.6 函數中的執行上下文

如下代碼展現了在函數體的語句執行以前,arguments變量和函數的參數都已經被賦值。從這裏能夠看出,函數每被調用一次,都會產生一個新的執行上下文環境。由於不一樣的調用可能就會有不一樣的參數

function fn(x) {
            console.log(arguments)
            console.log(x)
        }
        fn(10)
        fn(20)

clipboard.png

6.6 總結

全局代碼的上下文環境數據內容爲

普通變量(包括函數表達式),如: var a = 10; 聲明(默認賦值爲undefined)
函數聲明,如: function fn() { } 賦值
this 賦值

若是代碼段是函數體,那麼在此基礎上須要附加

參數 賦值
arguments 賦值
自由變量的取值做用域 賦值

6.7 參考

http://www.cnblogs.com/wangfu...

7.執行上下文棧

7.1 什麼是執行上下文棧

執行全局代碼時,會產生一個執行上下文環境,每次調用函數都又會產生執行上下文環境。當函數調用完成時,這個上下文環境以及其中的數據都會被消除,再從新回到全局上下文環境。處於活動狀態的執行上下文環境只有一個
這是一個壓棧出棧的過程——執行上下文棧

clipboard.png

7.2 上下文棧的壓棧、出棧過程

clipboard.png

  1. 在執行代碼以前,首先將建立全局上下文環境

    clipboard.png

  2. 而後是代碼執行。代碼執行到第12行以前,上下文環境中的變量都在執行過程當中被賦值。

    clipboard.png

  3. 執行到第13行,調用bar函數。
    跳轉到bar函數內部,執行函數體語句以前,會建立一個新的執行上下文環境。
    clipboard.png
  4. 並將這個執行上下文環境壓棧,設置爲活動狀態

    clipboard.png

  5. 執行到第5行,又調用了fn函數。進入fn函數,在執行函數體語句以前,會建立fn函數的執行上下文環境,並壓棧,設置爲活動狀態。

    clipboard.png

  6. 待第5行執行完畢,即fn函數執行完畢後,這次調用fn所生成的上下文環境出棧,而且被銷燬(已經用完了,就要及時銷燬,釋放內存)。

    clipboard.png

  7. 同理,待第13行執行完畢,即bar函數執行完畢後,調用bar函數所生成的上下文環境出棧,而且被銷燬(已經用完了,就要及時銷燬,釋放內存)。

    clipboard.png

8. this

8.1 判斷分析當前this

  1. 對於直接調用 foo 來講,無論 foo 函數被放在了什麼地方,this 必定是 window
  2. 對於 obj.foo() 來講,咱們只須要記住,誰調用了函數,誰就是 this,因此在這個場景下 foo 函數中的 this 就是 obj 對象
  3. 在構造函數模式中,類中(函數體中)出現的this.xxx=xxx中的this是當前類的一個實例
  4. call、apply和bind:this 是第一個參數
  5. 箭頭函數this指向:箭頭函數沒有本身的this,看其外層的是否有函數,若是有,外層函數的this就是內部箭頭函數的this,若是沒有,則this是window。

clipboard.png

8.2 this的5種狀況

8.2.1 直接調用foo()

function foo() {
            console.log(this)
        }
        var a = 1;
        foo()

clipboard.png

8.2.2 obj.foo()

function fn() {
            console.log(this)
        }
        var obj = {
            fn: fn
        }
        obj.fn();

clipboard.png

8.2.3 構造函數

function CreateJsPerson(name, age) {
            //this是當前類的一個實例p1
            this.name = name; //=>p1.name=name
            this.age = age; //=>p1.age=age
            console.log(this)
        }
        var p1 = new CreateJsPerson("尹華芝", 48);

clipboard.png

var age = 99;

        function PersonX() {
            this.age = 0;
            setTimeout(() => {
                this.age++;
                console.log(age)  //1
            }, 1000);
        }

        PersonX();
var x = 11;
        var obj = {
            x: 22,
            methods: {
                x: 33,
                say: function () {
                    console.log(this.x)
                },
                say2: () => {
                    console.log(this.x)
                }
            }
        }

        obj.methods.say(); //33
        obj.methods.say2(); //11

8.2.4 call、apply和bind

function add(c, d) {
            console.log(this)
            return this.a + this.b + c + d;
        }
        var o = {
            a: 1,
            b: 3
        };
        add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
        add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

clipboard.png

8.2.5 箭頭函數

<button id="btn1">箭頭函數this</button>
    <script>
        let btn1 = document.getElementById('btn1');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: function() {
                btn1.onclick = () => {
                    console.log(this); //obj
                };
            }
        };
        obj.getName();
    </script>

clipboard.png

8.3 參考

https://github.com/ljianshu/B...

9. 做用域和上下文環境

做用域在函數定義時就已經肯定了。而不是在函數調用時肯定

clipboard.png

9.1 程序執行時的,上下文環境

按照程序執行的順序,一步一步把各個上下文環境

  1. 在加載程序時,已經肯定了全局上下文環境,並隨着程序的執行而對變量就行賦值

    clipboard.png

  2. 程序執行到第27行,調用fn(10),此時生成這次調用fn函數時的上下文環境,壓棧,並將此上下文環境設置爲活動狀態。

    clipboard.png

  3. 行到第23行時,調用bar(100),生成這次調用的上下文環境,壓棧,並設置爲活動狀態

    clipboard.png

  4. 執行完第23行,bar(100)調用完成。則bar(100)上下文環境被銷燬。接着執行第24行,調用bar(200),則又生成bar(200)的上下文環境,壓棧,設置爲活動狀態。

    clipboard.png

  5. 執行完第24行,則bar(200)調用結束,其上下文環境被銷燬。此時會回到fn(10)上下文環境,變爲活動狀態。

    clipboard.png

  6. 執行完第27行代碼,fn(10)執行完成以後,fn(10)上下文環境被銷燬,全局上下文環境又回到活動狀態。

    clipboard.png

9.2 參考

http://www.cnblogs.com/wangfu...

10. 自由變量到做用域鏈

10.1 自由變量

在A做用域中使用的變量x,卻沒有在A做用域中聲明(即在其餘做用域中聲明的),對於A做用域來講,x就是一個自由變量

var x = 10;

        function fn() {
            var b = 20;
            console.log(x + b)   //這裏的x在這裏就是一個自由變量
        }
而取x的值時,就須要到另外一個做用域中取。到哪一個做用域中取呢?

要到建立這個函數的那個做用域中取值——是「建立」,而不是「調用」,切記切記

var x = 10;

        function fn() {
            console.log(x)    //10
        }

        function show(f) {
            var x = 20;
            (function() {
                f()
            })()
        }
        show(fn)

10.2 做用域鏈

若是跨了一步,還沒找到呢?——接着跨!——一直跨到全局做用域爲止。要是在全局做用域中都沒有找到,那就是真的沒有了。

這個一步一步「跨」的路線,咱們稱之爲——做用域鏈

取自由變量時的這個「做用域鏈」過程:(假設a是自由量)
  1. 如今當前做用域查找a,若是有則獲取並結束。若是沒有則繼續;
  2. 若是當前做用域是全局做用域,則證實a未定義,結束;不然繼續;
  3. 不是全局做用域,那就是函數做用域)將建立該函數的做用域做爲當前做用域;
  4. 跳轉到第一步。
實例

clipboard.png

第13行,fn()返回的是bar函數,賦值給x。執行x(),即執行bar函數代碼。取b的值時,直接在fn做用域取出。取a的值時,試圖在fn做用域取,可是取不到,只能轉向建立fn的那個做用域中去查找,結果找到了。

11. 閉包

11.1 什麼是閉包

閉包是指能夠訪問另外一個函數做用域變量的函數,通常是定義在外層函數中的內層函數。

11.2 爲何須要閉包呢

局部變量沒法共享和長久的保存,而全局變量可能形成變量污染,因此咱們但願有一種機制既能夠長久的保存變量又不會形成全局污染。

11.3 特色

  1. 佔用更多內存
  2. 不容易被釋放

11.4做用

  1. 保護
  2. 保存

11.5 什麼時候使用

既想反覆使用,又想避免全局污染

11.6 如何使用

  1. 定義外層函數,封裝被保護的局部變量。
  2. 定義內層函數,執行對外部函數變量的操做。
  3. 外層函數返回內層函數的對象,而且外層函數被調用,結果保存在一個全局的變量中。

11.7 實例

  1. 代碼執行前生成全局上下文環境,並在執行時對其中的變量進行賦值。此時全局上下文環境是活動狀態

    clipboard.png

  2. 執行第17行代碼時,調用fn(),產生fn()執行上下文環境,壓棧,並設置爲活動狀態。

    clipboard.png

  3. 執行完第17行,fn()調用完成

    按理說應該銷燬掉fn()的執行上下文環境,可是這裏不能這麼作。注意,重點來了:由於執行fn()時,返回的是一個函數。函數的特別之處在於能夠建立一個獨立的做用域。而正巧合的是,返回的這個函數體中,還有一個自由變量max要引用fn做用域下的fn()上下文環境中的max。所以,這個max不能被銷燬,銷燬了以後bar函數中的max就找不到值了。

    所以,這裏的fn()上下文環境不能被銷燬,還依然存在與執行上下文棧中。

  4. 執行到第18行時
    全局上下文環境將變爲活動狀態,可是fn()上下文環境依然會在執行上下文棧中。另外,執行完第18行,全局上下文環境中的max被賦值爲100。以下圖:

    clipboard.png

  5. 執行到第20行,執行f1(15),即執行bar(15),建立bar(15)上下文環境,並將其設置爲活動狀態

    clipboard.png

建立bar函數是在執行fn()時建立的。fn()早就執行結束了,可是fn()執行上下文環境還存在與棧中,所以bar(15)時,max能夠查找到。若是fn()上下文環境銷燬了,那麼max就找不到了

  1. 執行完20行就是上下文環境的銷燬過程

11.8 閉包面試解析實例

  1. 按道理,調用fn1()以後,就會銷燬fn1()
  2. 可是此時的result爲 function(){console.log(n)},(result爲fn1()的返回值)
  3. 若是要調用result(),可是result裏面的n是自由變量(函數的特別之處在於能夠建立一個獨立的做用域,result函數體內沒有定義n,要到他的上層做用域找),
  4. 在result的上層做用域fn1()裏找到了n
  5. 所以result依賴fn1()中的n,因此fn1()在調用後,並不能銷燬,fn1()中的n一直存在
function fn1() {
            var n = 0;
            return function() {
                console.log(n)
            }
        }
        var result = fn1();
        result()

11.9 參考

http://www.cnblogs.com/wangfu...
https://zhuanlan.zhihu.com/p/...
http://www.ruanyifeng.com/blo...
https://www.imooc.com/article/68898?block_id=tuijian_wz

12. new運算符的執行過程

12.1 步驟

  1. 用new Object() 的方式新建了一個對象 obj
  2. 取出第一個參數,就是咱們要傳入的構造函數。(此外由於 shift 會修改原數組,因此 arguments 會被去除第一個參數)
  3. 將 obj 的原型指向構造函數,這樣 obj 就能夠訪問到構造函數原型中的屬性
  4. 使用 apply,改變構造函數 this 的指向到新建的對象,這樣 obj 就能夠訪問到構造函數中的屬性
  5. 返回 obj,(判斷返回的值是否是一個對象,若是是一個對象,咱們就返回這個對象,若是沒有,咱們該返回什麼就返回什麼)

12.2 實例

function Person(name, age) {
            this.name = name;
            this.age = age;
            this.call = function() {
                alert(this.name + this.age)
            }
        }
        var p1 = new Person('jie', 12)
        p1.call()
function myNew3() {
            let obj = new Object();  //1
            let Constructor = [].shift.call(arguments);    //2
            obj.__proto__ = Constructor.prototype;        //3
            let result = Constructor.apply(obj, arguments);    //4
            if (result instanceof Object) {    //5
                return result
            } else {
                return obj;
            }
        }
        var p4 = myNew3(Person, 'wei', 14)
        p4.call()

12.3 參考

https://github.com/mqyqingfen...

13.call

13.1 定義

  1. 通俗的理解爲借用(一個對象沒有這個方法,可是別的對象有,不想重複代碼,因此借來用一下)
  2. call() 方法在使用一個指定的 this 值和若干個指定的參數值的前提下調用某個函數或方法。

13.2 實例

var obj = {
            value: '1'
        }

        function fn(name, age) {
            this.name = name;
            this.age = age;
            this.say = function() {
                alert(this.name + this.age)
            }
        }
         fn.call(obj, 'jie', 10)
        obj.say()

13.3 call的模擬實現

  1. 將函數設爲對象的屬性
  2. 執行該函數
  3. 刪除該函數
Function.prototype.call2 = function(context) {
            var context = context || window;
            context.fn = this;
            var args = [];
            for (var i = 1; i < arguments.length; i++) {
                args.push(`arguments[${i}]`)
            }
            var result = eval(`context.fn(${args})`)
            delete context.fn;
            return result;
        }
        fn.call2(obj, 'biao', 20)
        obj.say()

13.4 參考

https://www.cnblogs.com/moqiu...
https://github.com/mqyqingfen...

14. apply

apply與call類型,只是傳參不同

14.1 apply的模擬實現

var obj = {
            value: '1'
        }

        function fn(name, age) {
            this.name = name;
            this.age = age;
            this.say = function() {
                alert(this.name + this.age)
            }
        }
        
        Function.prototype.apply2 = function(context, arr) {
            var context = Object(context) || window;
            context.fn = this;
            var result;
            if (!arr) {
                result = context.fn()
            } else {
                var args = [];
                for (var i = 0; i < arr.length; i++) {
                    args.push(`arr[${i}]`)
                }
                result = eval(`context.fn(${args})`)
            }
            delete context.fn;
            return result;
        }
        fn.apply(obj, ['jie', 10])
        obj.say()
        fn.apply2(obj, ['biao', 20])
        obj.say()

15. bind

var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

// 返回了一個函數
var bindFoo = bar.bind(foo); 

bindFoo(); // 1

16. arguments

是一個對應於傳遞給函數的參數的類數組對象。

function foo(name, age, sex) {
            console.log(arguments)
        }
        foo('name', 'age', 'sex')

clipboard.png

function fn(...arguments) {
            console.log(arguments)
        }
        fn(1, 2, 3)

clipboard.png

17 繼承

17.1組合繼承

function Person() {
        this.name = 'jie'
        this.color = ['red', 'blue']
      }
      Person.prototype.say = function() {
        console.log(this.name)
      }

      function Student(id) {
        Person.call(this)
        this.id = id
      }

      Student.prototype = new Person()
      Student.prototype.constructor = Student

      var s1 = new Student('1')
      console.log(s1.name)
      console.log(s1.color)
      s1.say()
      s1.color.push('yellow')
      console.log(s1.color)

      var s2 = new Student('2')
      console.log(s2.name)
      console.log(s2.color)
      s2.say()
      s2.color.push('white')
      console.log(s2.color)

https://www.cnblogs.com/sarah...

18 異步

18.1 單線程

18.1.1 什麼是單線程

Javascript語言的執行環境是"單線程"(single thread)
所謂"單線程",就是指一次只能完成一件任務。若是有多個任務,就必須排隊,前面一個任務完成,再執行後面一個任務,以此類推。

單線程的優勢

這種模式的好處是實現起來比較簡單,執行環境相對單純

單線程的缺點

壞處是隻要有一個任務耗時很長,後面的任務都必須排隊等着,會拖延整個程序的執行。常見的瀏覽器無響應(假死),每每就是由於某一段Javascript代碼長時間運行(好比死循環),致使整個頁面卡在這個地方,其餘任務沒法執行。

解決單線程的缺點

爲了解決這個問題,Javascript語言將任務的執行模式分紅兩種:同步(Synchronous)和異步(Asynchronous)。

同步模式

後一個任務等待前一個任務結束,而後再執行,程序的執行順序與任務的排列順序是一致的、同步的

異步模式

每個任務有一個或多個回調函數(callback),前一個任務結束後,不是執行後一個任務,而是執行回調函數,後一個任務則是不等前一個任務結束就執行,因此程序的執行順序與任務的排列順序是不一致的、異步的。

異步模式的情景
http請求

$.ajax({
            url: '',
            success: function(data) {
                console.log(data)
            },
            error: function(error) {
                console.log(error)
            }
        })

18 異步

18.1 單線程

18.1.1 什麼是單線程

Javascript語言的執行環境是"單線程"(single thread)
所謂"單線程",就是指一次只能完成一件任務。若是有多個任務,就必須排隊,前面一個任務完成,再執行後面一個任務,以此類推。

單線程的優勢

這種模式的好處是實現起來比較簡單,執行環境相對單純

單線程的缺點

壞處是隻要有一個任務耗時很長,後面的任務都必須排隊等着,會拖延整個程序的執行。常見的瀏覽器無響應(假死),每每就是由於某一段Javascript代碼長時間運行(好比死循環),致使整個頁面卡在這個地方,其餘任務沒法執行。

解決單線程的缺點

爲了解決這個問題,Javascript語言將任務的執行模式分紅兩種:同步(Synchronous)和異步(Asynchronous)。

同步模式

後一個任務等待前一個任務結束,而後再執行,程序的執行順序與任務的排列順序是一致的、同步的

異步模式

每個任務有一個或多個回調函數(callback),前一個任務結束後,不是執行後一個任務,而是執行回調函數,後一個任務則是不等前一個任務結束就執行,因此程序的執行順序與任務的排列順序是不一致的、異步的。

18.1.2常見的異步操做

  1. 網絡請求,如ajax http.get
  2. IO 操做,如readFile readdir
  3. 定時函數,如setTimeout setInterval

18.2 回調函數(Callback)

// 1秒後打印出aa

function fn1(callback) {
            setTimeout(function() {
                callback();
            }, 1000)
        }
        fn1(fn2)

        function fn2() {
            console.log('aa')
        }

18.3 回調地獄

ajax(url, () => {
    // 處理邏輯
    ajax(url1, () => {
        // 處理邏輯
        ajax(url2, () => {
            // 處理邏輯
        })
    })
})

18.3.1 callback封裝

function ajaxFn(url, callback) {
            $.ajax({
                method: 'get',
                url: url,
                success: function(data) {
                    callback(data)
                },
                error: function(error) {
                    console.log(error)
                }
            })
        }
        ajaxFn('https://cnodejs.org/api/v1/topic/5433d5e4e737cbe96dcef312', (res) => {
            console.log(res)
            console.log('第一個請求完成')
            ajaxFn('https://cnodejs.org/api/v1/topics', (ress) => {
                console.log(ress)
                console.log('第二個請求完成')
            })
        })

clipboard.png

18.3.2 promise封裝

function promiseFn(url) {
            return new Promise((resolve, reject) => {
                $.ajax({
                    method: 'get',
                    url: url,
                    success: function(data) {
                        resolve(data)
                    },
                    error: function(error) {
                        reject(error)
                    }
                })
            })
        }
        promiseFn('https://cnodejs.org/api/v1/topic/5433d5e4e737cbe96dcef312')
            .then(res => {
                console.log(res)
                console.log('第一個請求完成')
                return promiseFn('https://cnodejs.org/api/v1/topics')
            })
            .then(res => {
                console.log(res)
                console.log('第二個請求完成')
            })
            .catch(err => {
                console.log(err)
            })

clipboard.png

18.3.3 async/await封裝

async function asyncFn(url) {
            return await new Promise((resolve, reject) => {
                $.ajax({
                    method: 'get',
                    url: url,
                    success: function(response) {
                        resolve(response);
                    },
                    error: function(error) {
                        reject(error);
                    }
                })
            })
        }
        async function start() {
            var result1 = await asyncFn('https://cnodejs.org/api/v1/topic/5433d5e4e737cbe96dcef312');
            var result2 = await asyncFn('https://cnodejs.org/api/v1/topics');
            console.log(result1)
            console.log('第一個請求完成')
            console.log(result2)
            console.log('第二個請求完成')
        }
        start()

clipboard.png

18.4 阻塞、非阻塞、同步、異步

18.4.1 生活中實例解析(熱水壺燒水)

在好久以前,科技尚未這麼發達的時候,若是咱們要燒水,須要把水壺放到火爐上,咱們經過觀察水壺內的水的沸騰程度來判斷水有沒有燒開

隨着科技的發展,如今市面上的水壺都有了提醒功能,當咱們把水壺插電以後,水壺水燒開以後會經過聲音提醒咱們水開了。

18.4.2 同步、異步

對於燒水這件事兒來講,傳統水壺的燒水就是同步的,高科技水壺的燒水就是異步的

18.4.3 阻塞、非阻塞

當你把水放到水壺裏面,按下開關後,你能夠坐在水壺前面,別的事情什麼都不作,一直等着水燒好。你還能夠先去客廳看電視,等着水開就行了。

對於你來講,坐在水壺前面等就是阻塞的,去客廳看電視等着水開就是非阻塞的。

18.4.4 阻塞、非阻塞和同步、異步的區別

阻塞、非阻塞說的是調用者(使用水壺的我),同步、異步說的是被調用者(水壺)。

19. 深淺拷貝

19.1 js 數據類型提及

  1. 基本數據類型保存在棧內存,
  2. 引用類型保存在堆內存中

19.1.1 棧內存,堆內存

var a = 1;//定義了一個number類型
var obj1 = {//定義了一個object類型
    name:'obj'
};

clipboard.png

19.1.2 基本類型的複製

var a = 1;
        var b = a;
        console.log(a) //1
        console.log(b)  //1
        b = 2;
        console.log(a)  //1
        console.log(b)  //2

賦值的時候,在棧內存中從新開闢內存,存放變量b,因此在棧內存中分別存放着變量a、b各自的值,修改時互不影響

19.1.3 引用類型的複製

var color1 = ['red', 'blue']
        var color2 = color1;
        console.log(color1)   //["red", "blue"]
        console.log(color2)   //["red", "blue"]
        color1.push('black');   
        color1.push('yellow');  
        console.log(color1) // ["red", "blue", "black", "yellow"]
        console.log(color2) //["red", "blue", "black", "yellow"]

color1與color2指向堆內存中同一地址的同一對象,複製的只是引用地址

clipboard.png

所以,對於引用類型的複製,簡單賦值無用,須要拷貝。拷貝存在兩種類型:深拷貝與淺拷貝

19.2 淺拷貝

淺拷貝只複製指向某個對象的指針,而不復制對象自己,新舊對象仍是共享同一塊內存
修改新對象會改到原對象

var person = {
            p1: {
                name: 'jie',
                age: 18
            },
            p2: 'biao'
        }

        var person1 = person;
        person1.p1.name = 'nine';

        console.log(person)    //nine
        console.log(person1)    //nine

19.3 深拷貝

深拷貝會另外創造一個如出一轍的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象
修改新對象不會改到原對象

function depClone(obj) {
            var result = JSON.parse(JSON.stringify(obj));
            return result;
        }
        
        var person = {
            p1: {
                name: 'jie',
                age: 18
            },
            p2: 'biao'
        }
        var person2 = depClone(person);
        person2.p1.name = 'nine';
        console.log(person)  //jie
        console.log(person2) //nine

19.4 參考

https://www.cnblogs.com/136as...
https://github.com/mqyqingfen...

20 DOM事件機制

20.1 DOM事件級別

clipboard.png

20.2 DOM 0級事件

20.2.1 最簡單的DOM 0級事件

<button type="button" onclick="fn" id="btn">點我試試</button>

<script>
    function fn() {
        alert('Hello World');
    }
</script>

20.2.2 onclick="fn"要不要加()

沒有()
button type="button" onclick="fn" id="btn">點我試試</button>
    <script>
        function fn() {
            alert('Hello World');
        }
        console.log(document.getElementById('btn').onclick);
    </script>

不彈出框(沒有執行alert())
clipboard.png

有()
button type="button" onclick="fn" id="btn">點我試試</button>
    <script>
        function fn() {
            alert('Hello World');
        }
        console.log(document.getElementById('btn').onclick);
    </script>

彈出框(執行alert())
clipboard.png

20.3 DOM2級事件

  1. 第一個參數是監聽動做
  2. 第二個參數監聽事件
  3. 第三個參數false冒泡,true捕獲
<button type="button" id="btn">點我試試</button>

<script>
    var btn = document.getElementById('btn');

    function fn() {
        alert('Hello World');
    }
    btn.addEventListener('click', fn, false);
    // 解綁事件,代碼以下
    // btn.removeEventListener('click', fn, false);  
</script>

20.4 DOM3級事件

  1. DOM3級事件在DOM2級事件的基礎上添加了更多的事件類型
  2. UI事件,當用戶與頁面上的元素交互時觸發,如:load、scroll
  3. 焦點事件,當元素得到或失去焦點時觸發,如:blur、focus
  4. 鼠標事件,當用戶經過鼠標在頁面執行操做時觸發如:dbclick、mouseup
  5. 滾輪事件,當使用鼠標滾輪或相似設備時觸發,如:mousewheel
  6. 文本事件,當在文檔中輸入文本時觸發,如:textInput
  7. 鍵盤事件,當用戶經過鍵盤在頁面上執行操做時觸發,如:keydown、keypress
  8. 合成事件,當爲IME(輸入法編輯器)輸入字符時觸發,如:compositionstart
  9. 變更事件,當底層DOM結構發生變化時觸發,如:DOMsubtreeModified

20.4 DOM事件流

20.4.1 爲何是有事件流?

假如在一個button上註冊了一個click事件,又在其它父元素div上註冊了一個click事件,那麼當咱們點擊button,是先觸發父元素上的事件,仍是button上的事件呢,這就須要一種約定去規範事件的執行順序,就是事件執行的流程。

瀏覽器在發展的過程當中出現了兩種不一樣的規範

  1. 9如下的IE瀏覽器使用的是事件冒泡,先從具體的接收元素,而後逐步向上傳播到不具體的元素。
  2. Netscapte採用的是事件捕獲,先由不具體的元素接收事件,最具體的節點最後才接收到事件。
  3. 而W3C制定的Web標準中,是同時採用了兩種方案,事件捕獲和事件冒泡均可以。

20.5 DOM事件模型

20.5.1 什麼是DOM事件模型

DOM事件模型分爲捕獲和冒泡。一個事件發生後,會在子元素和父元素之間傳播(propagation)。這種傳播分紅三個階段。

  1. 捕獲階段:事件從window對象自上而下向目標節點傳播的階段;
  2. 目標階段:真正的目標節點正在處理事件的階段;
  3. 冒泡階段:事件從目標節點自下而上向window對象傳播的階段。

20.5.2 事件捕獲

捕獲是從上到下,事件先從window對象,而後再到document(對象),而後是html標籤(經過document.documentElement獲取html標籤),而後是body標籤(經過document.body獲取body標籤),而後按照普通的html結構一層一層往下傳,最後到達目標元素。咱們只須要將addEventListener的第三個參數改成true就能夠實現事件捕獲

clipboard.png

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        body {
            margin: 0;
        }
        
        div {
            border: 1px solid #000;
        }
        
        #grandfather1 {
            width: 200px;
            height: 200px;
        }
        
        #parent1 {
            width: 100px;
            height: 100px;
            margin: 0 auto;
        }
        
        #child1 {
            width: 50px;
            height: 50px;
            margin: 0 auto;
        }
    </style>
</head>

<body>
    <div id="grandfather1">
        爺爺
        <div id="parent1">
            父親
            <div id="child1">兒子</div>
        </div>
    </div>
</body>
<script>
    var grandfather1 = document.getElementById('grandfather1'),
        parent1 = document.getElementById('parent1'),
        child1 = document.getElementById('child1');

    grandfather1.addEventListener('click', function fn1() {
        console.log('爺爺');

    }, true)
    parent1.addEventListener('click', function fn1() {
        console.log('爸爸');
    }, true)
    child1.addEventListener('click', function fn1() {
        console.log('兒子');
    }, true)
</script>

</html>

點擊id爲child1的div標籤時(兒子框),打印的結果是爺爺 => 爸爸 => 兒子,。

clipboard.png

20.5.2 事件冒泡

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        body {
            margin: 0;
        }
        
        div {
            border: 1px solid #000;
        }
        
        #grandfather1 {
            width: 200px;
            height: 200px;
        }
        
        #parent1 {
            width: 100px;
            height: 100px;
            margin: 0 auto;
        }
        
        #child1 {
            width: 50px;
            height: 50px;
            margin: 0 auto;
        }
    </style>
</head>

<body>
    <div id="grandfather1">
        爺爺
        <div id="parent1">
            父親
            <div id="child1">兒子</div>
        </div>
    </div>
</body>
<script>
    var grandfather1 = document.getElementById('grandfather1'),
        parent1 = document.getElementById('parent1'),
        child1 = document.getElementById('child1');

    grandfather1.addEventListener('click', function fn1() {
        console.log('爺爺');

    }, false)
    parent1.addEventListener('click', function fn1() {
        console.log('爸爸');
    }, false)
    child1.addEventListener('click', function fn1() {
        console.log('兒子');
    }, false)
</script>

</html>

點擊id爲child1的div標籤時(兒子框),打印的結果是兒子=>爸爸=>爺爺

20.6 事件代理(事件委託)

20.6.1 事件代理含義和爲何要優化?

因爲事件會在冒泡階段向上傳播到父節點,所以能夠把子節點的監聽函數定義在父節點上,由父節點的監聽函數統一處理多個子元素的事件。這種方法叫作事件的代理(delegation)。

舉個例子,好比一個宿舍的同窗同時快遞到了,一種方法就是他們都傻傻地一個個去領取,還有一種方法就是把這件事情委託給宿舍長,讓一我的出去拿好全部快遞,而後再根據收件人一一分發給每一個宿舍同窗;
在這裏,取快遞就是一個事件,每一個同窗指的是須要響應事件的 DOM 元素,而出去統一領取快遞的宿舍長就是代理的元素,因此真正綁定事件的是這個元素,按照收件人分發快遞的過程就是在事件執行中,須要判斷當前響應的事件應該匹配到被代理元素中的哪個或者哪幾個。

那麼利用事件冒泡或捕獲的機制,咱們能夠對事件綁定作一些優化。 在JS中,若是咱們註冊的事件愈來愈多,頁面的性能就愈來愈差,由於:

  1. 函數是對象,會佔用內存,內存中的對象越多,瀏覽器性能越差
  2. 註冊的事件通常都會指定DOM元素,事件越多,致使DOM元素訪問次數越多,會延遲頁面交互就緒時間。
  3. 刪除子元素的時候不用考慮刪除綁定事件

20.6.2 優勢

  1. 減小內存消耗,提升性能
    若是給每一個列表項一一都綁定一個函數,那對於內存消耗是很是大的,效率上須要消耗不少性能。藉助事件代理,咱們只須要給父容器ul綁定方法便可,這樣無論點擊的是哪個後代元素,都會根據冒泡傳播的傳遞機制,把容器的click行爲觸發,而後把對應的方法執行,根據事件源,咱們能夠知道點擊的是誰,從而完成不一樣的事。
  2. 動態綁定事件
    在不少時候,咱們須要經過用戶操做動態的增刪列表項元素,若是一開始給每一個子元素綁定事件,那麼在列表發生變化時,就須要從新給新增的元素綁定事件,給即將刪去的元素解綁事件,若是用事件代理就會省去不少這樣麻煩。

20.6.3 代碼實例

<ul id="list">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
    </ul>

    <script>
        // 給父層元素綁定事件
        document.getElementById('list').addEventListener('click', function(e) {
            // 兼容性處理
            var event = e || window.event;
            var target = event.target || event.srcElement;
            // 判斷是否匹配目標元素
            if (target.nodeName.toLocaleLowerCase() === 'li') {
                console.log('the content is: ', target.innerHTML);
            }
        });
    </script>

這是常規的實現事件委託的方法,可是這種方法有BUG,當監聽的元素裏存在子元素時,那麼咱們點擊這個子元素事件會失效,因此咱們能夠聯繫文章上一小節說到的冒泡事件傳播機制來解決這個bug。改進的事件委託代碼:

<ul id="list">
        <li>1 <span>aaaaa</span></li>
        <li>2 <span>aaaaa</span></li>
        <li>3 <span>aaaaa</span></li>
        <li>4</li>
    </ul>

    <script>
        document.getElementById('list').addEventListener('click', function(e) {
            // 兼容性處理
            var event = e || window.event;
            var target = event.target || event.srcElement;
            // 判斷是否匹配目標元素
            /* 從target(點擊)元素向上找currentTarget(監聽)元素,
            找到了想委託的元素就觸發事件,沒找到就返回null */
            while (target.tagName !== 'LI') {

                if (target.tagName === 'UL') {
                    target = null
                    break;
                }
                target = target.parentNode
            }
            if (target) {
                // console.log('你點擊了ul裏的li')
                console.log('the content is: ', target.innerHTML);
            }
        });
    </script>

20.7 Event對象常見的方法和屬性

20.7.1 event. preventDefault()

preventDefault阻止默認行爲
若是調用這個方法,默認事件行爲將再也不觸發。什麼是默認事件呢?例如表單一點擊提交按鈕(submit)刷新頁面、a標籤默認頁面跳轉或是錨點定位等。

a標籤默認頁面跳轉
<a id="test" href="http://www.google.com">連接</a>
<script>
    test.onclick = function(e){
        e = e || window.event;
        e.preventDefault();
    }
</script>
輸入框最多隻能輸入六個字符
<input type="text" id='tempInp'>
    <script>
        tempInp.onkeydown = function(ev) {
            ev = ev || window.event;
            let val = this.value.trim() //trim去除字符串首位空格(不兼容)
                // this.value=this.value.replace(/^ +| +$/g,'') 兼容寫法
            let len = val.length
            if (len >= 6) {
                this.value = val.substr(0, 6);
                //阻止默認行爲去除特殊按鍵(DELETE\BACK-SPACE\方向鍵...)
                let code = ev.which || ev.keyCode;
                if (!/^(46|8|37|38|39|40)$/.test(code)) {
                    ev.preventDefault()
                }
            }
        }
    </script>

20.7.2 event.stopPropagation()

stopPropagation中止冒泡
event.stopPropagation() 方法阻止事件冒泡到父元素,阻止任何父事件處理程序被執行

<body>
    <div id="grandfather1">
        爺爺
        <div id="parent1">
            父親
            <div id="child1">兒子</div>
        </div>
    </div>
</body>
<script>
    var grandfather1 = document.getElementById('grandfather1'),
        parent1 = document.getElementById('parent1'),
        child1 = document.getElementById('child1');

    grandfather1.addEventListener('click', function fn1() {
        console.log('爺爺');

    }, false)
    parent1.addEventListener('click', function fn1() {
        console.log('爸爸');
        event.stopPropagation()
    }, false)
    child1.addEventListener('click', function fn1() {
        console.log('兒子');
    }, false)

點擊兒子的時候,打印兒子,爸爸
點擊爸爸的時候,打印爸爸

20.7.3 stopImmediatePropagation

既能阻止事件向父元素冒泡,也能阻止元素同事件類型的其它監聽器被觸發。而 stopPropagation 只能實現前者的效果

<button id="btn">點我試試</button>
    <script>
        const btn = document.querySelector('#btn');
        btn.addEventListener('click', event => {
            console.log('btn click 1');
            event.stopImmediatePropagation();
        });
        btn.addEventListener('click', event => {
            console.log('btn click 2');
        });
        document.body.addEventListener('click', () => {
            console.log('body click');
        });
    </script>

只打印console.log('btn click 1');

<body>
    <div id="grandfather1">
        爺爺
        <div id="parent1">
            父親
            <div id="child1">兒子</div>
        </div>
    </div>
</body>
<script>
    var grandfather1 = document.getElementById('grandfather1'),
        parent1 = document.getElementById('parent1'),
        child1 = document.getElementById('child1');

    grandfather1.addEventListener('click', function fn1() {
        console.log('爺爺');

    }, false)
    parent1.addEventListener('click', function fn1() {
        console.log('爸爸');
        event.stopImmediatePropagation();
    }, false)
    child1.addEventListener('click', function fn1() {
        console.log('兒子');
    }, false)
</script>

點擊兒子的時候,打印兒子,爸爸
點擊爸爸的時候,打印爸爸

20.8 參考

https://juejin.im/post/5c71e8...
https://github.com/ljianshu/B...

21 隨機生成一個長度爲10的範圍0-100不重複的數組

function random() {
        var arr = []
        for (var i = 0; i < 100; i++) {
          //生成循環100次,生成100個數字。該方法最大的弊端,爲了不有重複的狀況致使數組不足10個元素,因此生成較多的數字
          var num = Math.floor(Math.random() * 100) //生成0-100的隨機整數
          if (arr.length == 0) {
            arr.push(num) //數組爲空時直接放入數組
          } else {
            for (var j = 0; j < arr.length; j++) {
              //循環已存在的數組
              if (arr.join(',').indexOf(num) < 0 && arr.length < 10) {
                //判斷已存在數組中是否已有剛生成的數字,如沒有且數組長度不足10纔將num放入arr
                arr.push(num) //這樣又會致使生成的大部分數字被arr.length <= 10排除掉了,浪費性能
              }
            }
          }
        }
        return arr
      }
let set = new Set()
      while (set.size < 10) {
        //多少
        set.add(Math.round(Math.random() * 10) + 0) //最大值,最小值
      }
      let arr4 = Array.from(set)
      console.log(arr4)
var arr5 = new Array()
      while (arr5.length < 10) {
        var num = Math.round(180 * Math.random()) + 20
        var exists = false
        for (var i = 0, l = arr5.length; i < l; i++) {
          if (arr5[i] == num) {
            //判斷是否已經存在
            exists = true //存在的話將true傳給exists
          }
        }
        if (!exists) {
          //如今exist是true,!exists就是false,因此不執行這個if下面代碼。
          arr5.push(num)
        }
      }
      console.log(arr5)

22 js字符串中如何判斷出現最多的字符是哪個

var str = "abbcccddddeeee";

        function max() {
            var json = {};
            for (var i = 0; i < str.length; i++) {
                var k = str[i]; //k是全部字符,字符串也跟數組同樣能夠經過中括號下標方法取到每一個子元素
                if (json[k]) {
                    json[k]++; //json裏有這個字符時,把這個字符的數量+1,
                } else {
                    json[k] = 1; //不然把這個字符的數量設爲1
                }
            }
            console.log(json)
            var num = 0;
            var value = null;
            for (var k in json) { //s、f、g、t、d
                if (json[k] > num) {
                    num = json[k];
                    value = k;
                }
            }
            alert("出現最多的字符是:" + value + ',出現次數是:' + num);
        };
        max(str);

23. js的四種for循環

參考

http://www.javashuo.com/article/p-asgepdjs-ce.html

https://juejin.im/entry/5a1654e951882554b8373622

24. var,let,const

24.1 字面理解

  1. let,變量,用於替代var
  2. const,常量

24.2 爲何引入了let,const

ES5只有全局做用域和函數做用域,沒有塊級做用域。

24.3

var a = 1;
        function fn(){
            var a = 2;
            console.log(a)  //2
        }
        fn()
        console.log(a)  //1

        var a = 1;
        function fn(){
            a = 2;
            console.log(a)  //2
        }
        fn()
        console.log(a)  //2


        var a = 1;
        function fn(a){
            var a = 2;  //此時a爲傳進來的參數
            console.log(a)  //2
        }
        fn(a)
        console.log(a)  //1

        var a = 1;
        function fn(a){
            a = 2;//此時a爲傳進來的參數
            console.log(a)  //2
        }
        fn(a)
        console.log(a)  //1
function f1(a) {
            console.log(a); //10
            var a=1;
            console.log(a); //1
            console.log(arguments[0]);  //1
        }

        f1(10)
var a = 0;
        function fn() {
            console.log(a) // undefined
            // console.log(b) // defined
            var a = b = 1;  //b至關於全局變量
            console.log(b) //1
        }
        fn() 
        console.log(a) //0
        console.log(b) //1
var a = 0;

        function fn(a) {
            console.log(a)  //0
            var a = b = 1;
            console.log(b) //1
        }
        fn(a)
        console.log(a)  //0
        console.log(b)  //1

24.4 參考JavaScript 的變量做用域

25. 擴展運算符rest運算符

25.1 參考

https://www.cnblogs.com/mengfangui/archive/2017/12/15/8041317.html

26. 箭頭函數和普通函數的區別

箭頭函數和普通函數的區別

27. require和import的區別

http://www.javashuo.com/article/p-dlzlsjtj-da.html

相關文章
相關標籤/搜索