2023-03-23_DOMDAY08-JS高級(jí)-遞歸

1. bind方法

bind() 函數(shù)會(huì)創(chuàng)建一個(gè)新函數(shù)(稱為綁定函數(shù))

  • bind是ES5新增的一個(gè)方法
  • 傳參和call類似
  • 不會(huì)執(zhí)行對(duì)應(yīng)的函數(shù),call或apply會(huì)自動(dòng)執(zhí)行對(duì)應(yīng)的函數(shù)
  • 返回對(duì)函數(shù)的引用
  • 語法 fun.bind(thisArg[, arg1[, arg2[, ...]]])
<script>
    var obj = {
            name: "阿馬"
    }
    function add(a, b) {
            console.log(this);
            console.log(a + b)
    }
    var new1 = add.bind(obj, 1, 2)
    new1();
    //返回的不是同一個(gè)函數(shù)
    console.log(new1 === add);
</script>

2. querySelector和getxxxByxxx的區(qū)別

getXXXByXXX 獲取的是動(dòng)態(tài)集合,querySelector獲取的是靜態(tài)集合。

簡(jiǎn)單的說就是,動(dòng)態(tài)就是選出的元素會(huì)隨文檔改變,靜態(tài)的不會(huì),取出來之后就和文檔的改變無關(guān)了。

3. 函數(shù)的遞歸

前提:arguments.callee 打印出來的就是函數(shù)本身,它可以在函數(shù)遞歸調(diào)用的時(shí)候,派上用場(chǎng)
arguments.callee 打印出來的就是函數(shù)本身,它可以在函數(shù)遞歸調(diào)用的時(shí)候,派上用場(chǎng)
function fn(){
console.log(arguments.length);
console.log(arguments.callee);
}
fn();

能用遞歸的地方都可以不用。
函數(shù)的遞歸調(diào)用,就是函數(shù)在內(nèi)部自己調(diào)自己
函數(shù)的遞歸調(diào)用是一把雙刃劍,如果設(shè)計(jì)的好,可以幫我們簡(jiǎn)單的處理事情,如果設(shè)計(jì)不好就是災(zāi)難

  • 函數(shù)的遞歸要想設(shè)計(jì)好必須有兩個(gè)條件
    • 必須有一個(gè)明顯的結(jié)束條件,不能一直遞歸下去
    • 每一次調(diào)用都要有一個(gè)趨近結(jié)束條件的趨勢(shì)
// 遞歸實(shí)現(xiàn)階乘
 function f(n){
       if(n == 1){
            return 1
       }
            return n * f(n-1);
       }
var a = f(5);
console.log(a); // 120

4. 終極原型鏈

任何的函數(shù)對(duì)象的prototype都和這個(gè)函數(shù)對(duì)象實(shí)例化的對(duì)象proto都指向同一個(gè)對(duì)象

原型鏈.jpg
終極原型鏈.png

5. 閉包

為什么要學(xué)習(xí)閉包?
先必須理解Javascript特殊的變量作用域。

變量的作用域無非就是兩種:全局變量和局部變量。

  • 全局變量 可以在局部作用域內(nèi)訪問
  • 局部變量 不可以在全局作用域內(nèi)訪問的
  • 函數(shù)內(nèi)部聲明變量的時(shí)候,一定要使用var命令。如果不用的話,實(shí)際上聲明了一個(gè)全局變量
  • 閉包的產(chǎn)生條件/使用步驟
    1. 在外部函數(shù)f1內(nèi) 嵌套一個(gè)內(nèi)部函數(shù)f2
    2. 內(nèi)部函數(shù)f2 引用外部函數(shù)f1的局部變量a
    3. 外部函數(shù)f1 返回內(nèi)部函數(shù)f2的函數(shù)體
    4. 內(nèi)部函數(shù)f2和外部函數(shù)f1都要執(zhí)行
 function f1(){
    var a = 1;
    function f2(){
       return a;
    }
    return f2;
}
// 返回的是就是f2的函數(shù)體
var result = f1();
var aaaa = result();
console.log(aaaa);
  • 閉包到底是什么?

    • 理解一: 閉包是嵌套的內(nèi)部函數(shù)(絕大部分人)
    • 理解二: 閉包是包含外部函數(shù)的局部變量的對(duì)象(極少數(shù)人)
    • 理解三: 所謂的閉包是一個(gè)引用關(guān)系,該引用關(guān)系存在于內(nèi)部函數(shù)中,引用的是外部函數(shù)的局部變量的對(duì)象(深入理解)
  • 常見的閉包

    • 將函數(shù)作為另一個(gè)函數(shù)的返回值
    • 將內(nèi)部函數(shù)作為外部函數(shù)的返回值
  • 閉包的作用 ( 企業(yè)開發(fā)中我們不允許使用全局變量 )

    • 延長外部函數(shù)變量的生命周期
    • 讓函數(shù)外部可以操作(讀寫)到函數(shù)內(nèi)部的數(shù)據(jù)(變量/函數(shù))/函數(shù)外部可以引用函數(shù)內(nèi)部的變量
    • 注意: 瀏覽器為了性能,后期將外部函數(shù)中不被內(nèi)部函數(shù)使用的變量清除了
function buyLip(){
    var money = 500;
    function f2(){
        money -= 100;
        console.log(money);
    }
    return f2;
}
var result = buyLip();
result(); // 400
result(); // 300
  • 閉包使用中的坑點(diǎn) ( ()()會(huì)重置初始化原始外部變量的值 )
function buyLip(){
    var money = 500;
    function f2(){
        money -= 100;
        console.log(money);
    }
    return f2;
}
buyLip()(); // 500
buyLip()(); // 500
  • 閉包的缺點(diǎn)和解決(內(nèi)存泄漏和內(nèi)存溢出)
    • 內(nèi)存泄漏 : 內(nèi)存無法釋放;
    • 內(nèi)存溢出 : 內(nèi)存被撐爆;
    • f = null; 解決方式;
function fn(){
    var a = 0;
    function fn1(){
        a++;
        console.log(a);
    }
    return fn1;
}
var f =  fn();
f();
f();
f = null;//釋放閉包

6. 面向?qū)ο?/h1>

面向?qū)ο笕筇匦裕悍庋b、繼承、多態(tài)

  • 原型繼承-方法繼承
    • 讓父類的實(shí)例作為子類的原型,將子類的原型構(gòu)造器補(bǔ)充完整 (為了讓子類繼承方法)
<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8" />
        <title>面向?qū)ο?原型繼承-方法繼承</title>
    </head>
    <body>
        <script>
            function Dog(){
            }
            Dog.prototype.eat = function(){
                console.log('翔');
            }

            function Teddy(){
            }
            Teddy.prototype = new Dog();
            Teddy.prototype.constructor = Teddy;

            function ChaiQuan(){
            }
            ChaiQuan.prototype = new Dog();
            ChaiQuan.prototype.constructor = ChaiQuan;

            var t = new Teddy();
            t.eat();
            var c = new ChaiQuan();
            c.eat();

        </script>
    </body>
</html>
原型繼承(方法繼承).png
  • 構(gòu)造器(函數(shù))繼承-屬性繼承

    • 在子類當(dāng)中去調(diào)用父類的構(gòu)造函數(shù)(為了讓子類繼承屬性)
  • 組合繼承

    • 原型繼承方法,借用構(gòu)造函數(shù)繼承屬性一起使用
  • 方法重寫和方法重載(多態(tài)的表現(xiàn)形式)
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
            //父類
            function Dog(name,age){
                this.name = name;
                this.age = age;
            }
            
            //子類
            function Teddy(name,age){
//              this.name = name;
//              this.age = age;
                //借助父類的構(gòu)造函數(shù)實(shí)現(xiàn)屬性的繼承
                Dog.call(this,name,age);
            }
            
            Dog.prototype.run = function(){
                console.log('跑的很快~');
            }
            
            //為了讓子類去繼承父類原型當(dāng)中的方法
            //我們需要用到原型繼承,原型繼承主要就是為了讓子類繼承方法使用的
            //讓子類的原型變成父類的一個(gè)實(shí)例
            //手動(dòng)給原型對(duì)象添加一個(gè)構(gòu)造器,因?yàn)樵蛯?duì)象里面都是有構(gòu)造器的,指向和自己匹配的構(gòu)造函數(shù)
            Teddy.prototype = new Dog();
            Teddy.prototype.constructor = Teddy;
            
            
            //方法重寫和方法重載   是多態(tài)的兩個(gè)表現(xiàn)形式
            //方法重寫;和父類同名方法功能不同,被稱作方法重寫
            Teddy.prototype.run = function(flag){
                //方法重載
                if(typeof flag === 'number'){
                    console.log('跑的很慢~');
                }else{
                    console.log('跑的很快~');
                }
            }
            

            var d1 = new Dog('旺財(cái)',3);
            d1.run();
            
            var t1 = new Teddy('小黑',2);
            t1.run(10);
            
            console.log(t1);
            
        </script>
    </body>
</html>

7. web Workers

  • H5規(guī)范提供了js分線程的實(shí)現(xiàn), 取名為: Web Workers

  • 相關(guān)API

    • Worker: 構(gòu)造函數(shù), 加載分線程執(zhí)行的js文件

    • Worker.prototype.onmessage: 用于接收另一個(gè)線程的回調(diào)函數(shù)

    • Worker.prototype.postMessage: 向另一個(gè)線程發(fā)送消息

    • 每個(gè)線程可以向不同線程發(fā)送消息 也可以接收不同線程傳來的消息

    • 主線程操作

      • 發(fā)送消息: worker.postMessage(消息可以是任何數(shù)據(jù))

      • 接受消息: worker.onmessage = function(e){

                  console.log(e.data)//接收到的消息或者數(shù)據(jù)在事件對(duì)象的data屬性當(dāng)中
        
              }
        
    • 子線程操作

      • 發(fā)送消息: worker.postMessage(消息可以是任何數(shù)據(jù))

      • 接受消息: worker.onmessage = function(e){

                  console.log(e.data)//接收到的消息或者數(shù)據(jù)在事件對(duì)象的data屬性當(dāng)中
        
              }
        
  • 不足

    • worker內(nèi)代碼不能操作DOM
    • 不能跨域加載JS
    • 不是每個(gè)瀏覽器都支持這個(gè)新特性
  • 計(jì)算得到fibonacci數(shù)列中第n個(gè)數(shù)的值

    • 在主線程計(jì)算: 當(dāng)位數(shù)較大時(shí), 會(huì)阻塞主線程, 導(dǎo)致界面卡死
function fib(n){
    if(n <= 2){
        return 1;
    }
    return fib(n- 1) + fib(n - 2);
}
  • js代碼-myThread.js :
function fib(n){
    if(n <= 2){
        return 1;
    }
    return fib(n- 1) + fib(n - 2);
}
            
self.onmessage = function(e){
    e = e || window.event;
//  console.log(e.data);//就 可以拿到主線程給我 發(fā)過來的消息內(nèi)容
    
    var result = fib(e.data);
    self.postMessage(result);//分線程接受到主線程的消息,然后開始計(jì)算,把計(jì)算后的記過發(fā)消息再給主線程
}
  • html代碼 :
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
            //第一步:實(shí)例化一個(gè)對(duì)象,初始化主線程和分線程關(guān)聯(lián)對(duì)象
            
            
            var worker = new Worker('./myThread.js');
            
            worker.postMessage(100);//主線程發(fā)送消息給分線程,消息內(nèi)容可以是任意類型
            
            worker.onmessage = function(e){
                e = e || window.event;
                //等待接受分線程計(jì)算完的結(jié)果
                console.log(e.data);
            }

            console.log('大哥 js Ending~');
    
        </script>
    </body>
</html>

面試題

// 當(dāng)發(fā)生連等的時(shí)候  那么屬性的優(yōu)先級(jí)更高
var a = { n: 1 };
var b = a;
a.x = {n:2}
a = {n:2}
a.x = a = { n: 2 };
console.log(a.x); // 報(bào)錯(cuò)
console.log(b.x); // {n:2}
var b1 = {
        b2:[2,'atguigu',console.log],
        b3:function () {
        alert('hello')
        }
}
console.log(b1, b1.b2, b1.b3); // {b2: Array(3), b3: ?}  b2: (3) [2, 'atguigu', ?]  b3: ? ({alert('hello')})
console.log(b1 instanceof Object, typeof b1); // true 'object'
console.log(b1.b2 instanceof Array, typeof b1.b2); // true  'object'
console.log(b1.b3 instanceof Function, typeof b1.b3); // true  'function'
console.log(typeof b1.b2[2]); // 'function'
console.log(typeof b1.b2[2]('atguigu')); // 'atguigu' undefined 
b1.b2[2]('atguigu'); // 'atguigu'
var a = {};
var obj1 = {
    m: 2,
};
var obj2 = {
    n: 2,
};
var obj3 = function () {};

a[obj1] = 4;
a[obj2] = 5;
a.name = 'kobe';
a[obj3] = 6;

console.log(a[obj1]); // 5
console.log(a); // {[object Object]: 5, name: 'kobe', function () {}: 6}
var name = 'The Window';
var object = {
    name: 'My Object',
    getNameFunc: function () {
        return function () {
            return this.name;
        };
    },
};
 console.log(object.getNameFunc()); //  f () { return this.name }
console.log(object.getNameFunc()()); // The Window
var name2 = 'The Window';
var object2 = {
    name2: 'My Object',
    getNameFunc: function () {
        var that = this;
        return function () {
            return that.name2;
        };
    },
};
console.log(object2.getNameFunc()); // f () {return that.name2;}
console.log(object2.getNameFunc()()); // My Object
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容