JavaScript高級知識點整理(代碼含解析)

JavaScript 高級筆記

面向對象與面向過程

面向過程是指分析結局問題的步驟,然後按照分析的步驟一步步實現.

面向對象是指把事物分解成一個個對象,然後由對象之間分工合作.

  面向過程 面向對象
優點 性能比面向對象高,適合跟硬件聯繫很緊密的東西,例如單片機就採用的面向過程編程。 易維護、易複用、易擴展,由於面向對象有封裝、繼承、多態性的特性,可以設計出低耦合的系統,使系統 更加靈活、更加易於維護
缺點 不易維護、不易複用、不易擴展 性能比面向過程低

面向對象的特點

封裝 繼承 多態

對象和類

對象是由屬性和方法組成:是一個無序鍵值對的集合,是指一個具體的事物

(對象如果想批量生產出來,可以用類似現實生活中的圖紙和車的關係一樣

圖紙:類 ; 車:對象)

屬性:事物的特徵,在對象中用屬性來表示(常用名詞)

方法:事物的行爲,在對象中用方法來表示(常用動詞)

//創建對象的三種簡單方式:
1.字面量:
var obj={
    age :18,//屬性名:屬性值
    uname :'Amy'
    fn:function(){
        console.log('你好')
    }//方法名:方法
}
2.構造函數:
function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayHi=function(){
        console.log('你好')
    }
}
var xm = new Person('小明',20)
​
3.自定義構造函數:
var obj = new Object();
obj.name='小明';//給對象追加屬性
obj.sayHi=function(){
        console.og('你好')
    }//給對象追加方法

 

  工廠模式 自定義構造函數
不同 函數名是小寫 有new, 有返回值 new之後的對象是當前的對象 直接調用函數就可以創建對象 函數名是大寫(首字母) 沒有new 沒有返回值 this是當前的對象 通過new的方式來創建對象
相同 都是函數,都可以創建對象,都可以傳入參數  
<script>
        // 工廠模式
        function fn(name, age) {
            var obj = {}
            obj.name = name
            obj.age = age
            return obj;
​
        }
        var zxy = fn('張學友', 20)
        console.log(zxy);
        // 構造函數 ----->實例化對象
        function Star(name, age) {
            this.name = name
            this.age = age
            this.sing=function(){
                console.log('我會唱歌')
            }
        }
        var ldh = new Star('劉德華', 20)
        console.log(ldh);
    /*
    實例對象和構造函數之間的關係:
    * 1. 實例對象是通過構造函數來創建的---創建的過程叫實例化
    如何判斷對象是不是這個數據類型?
       1) 通過構造器的方式 實例對象.構造器==構造函數名字  
       hero.constructor === Hero
       2) 對象 instanceof 構造函數名字儘可能的使用第二種方式來識別
   */
    </script>

實例成員與靜態成員

//實例成員是對象的成員
hero.name = 'zs';
//靜態成員是直接給構造函數添加的成員
Hero.version = '1.0';
​
// 靜態成員不能使用對象的方式來調用
console.log(hero.version);
​
// 靜態成員使用構造函數來調用
console.log(Hero.version);
​
//實例成員 是構造函數內部通過this添加的成員 如下列代碼中uname age sing 就是實例成員,實例成員只能通過實例化的對象來訪問
 function Star(uname, age) {
     this.uname = uname;
     this.age = age;
     this.sing = function() {
     console.log('我會唱歌');
    }
}
var ldh = new Star('劉德華', 18);
console.log(ldh.uname);//實例成員只能通過實例化的對象來訪問
​
//靜態成員  在構造函數本身上添加的成員  如下列代碼中 sex 就是靜態成員,靜態成員只能通過構造函數來訪問
 function Star(uname, age) {
     this.uname = uname;
     this.age = age;
     this.sing = function() {
     console.log('我會唱歌');
    }
}
Star.sex = '男';
var ldh = new Star('劉德華', 18);
console.log(Star.sex);//靜態成員只能通過構造函數來訪問

ES6 中新增加了類的概念,可以使用 class 關鍵字聲明一個類,之後以這個類來實例化對象。類抽象了對象的公共部分,它泛指某一大類(class)對象特指某一個,通過類實例化一個具體的對象.

1.創建類:語法
//使用class關鍵字
class name {
    //class student
}
//使用定義的類創建實例 使用new關鍵字
var xm = new name();
​
2.示例
 class Star {
            // constructor在NEW的時候會自動被調用,並且只能調用一次
            // 公共的方法在new的時候不會被調用,只能我們自己主動調用
     // 類的共有屬性放到 constructor 裏面 constructor是 構造器或者構造函數
            constructor(uname, age) {
                this.uname = uname;
                this.age = age;
            }
            // 公共方法
            // 類中所有的函數都不需要用function,並且不需要逗號隔開
            sing(song) {
                console.log('我會唱' + song);
            }
        }
        //利用類創建對象 new
        var ldh = new Star('劉德華', 20);
​
        ldh.sing('笨小孩');
/*
注意點
1. 通過class 關鍵字創建類, 類名我們還是習慣性定義首字母大寫
2. 類裏面有個constructor 函數,可以接受傳遞過來的參數,同時返回實例對象
3. constructor 函數 只要 new 生成實例時,就會自動調用這個函數, 如果我們不寫這個函數,類也會自動生成這個函數
4. 多個函數方法之間不需要添加逗號分隔
5. 生成實例 new 不能省略
6. 語法規範, 創建類 類名後面不要加小括號,生成實例 類名後面加小括號, 構造函數不需要加function
*/

類的繼承

語法:
// 父類
class Father{   
} 
​
// 子類繼承父類
class  Son  extends Father {  
}   
​
示例:
 //繼承
      class Father {
            constructor(uname, age) {
                this.uname = uname;
                this.age = age;
                this.money1 = 300;
            }
            sing1() {
                return '我會唱歌';
            }
            eat() {
                console.log('我還會喫飯');
            }
            sleep() {
                console.log('我會睡覺');
            }
        }
        // sing1()
        // extends繼承父類
        class Son extends Father {
            constructor(uname, age, money) {
                // super()調用父類的 constructor構造函數
                super(uname, age, money);
                this.money = money;
            }
            hobby() {
                console.log('我喜歡看書' + super.sing1());
            }
            salary() {
                console.log(this.money + this.money1);
            }
        }
        var xm = new Son('小明', 22, 100);
        // console.log(xm);
        xm.salary();//400
        xm.hobby();//我喜歡看書我會唱歌
/*
注意:
1. 繼承中,如果實例化子類輸出一個方法,先看子類有沒有這個方法,如果有就先執行子類的
2. 繼承中,如果子類裏面沒有,就去查找父類有沒有這個方法,如果有,就執行父類的這個方法(就近原則)
3. 如果子類想要繼承父類的方法,同時在自己內部擴展自己的方法,利用super 調用父類的構造函數,super 必須在子類this之前調用
4. 時刻注意this的指向問題,類裏面的共有的屬性和方法一定要加this使用.
   1. constructor中的this指向的是new出來的實例對象 
   2. 自定義的方法,一般也指向的new出來的實例對象
   3. 綁定事件之後this指向的就是觸發事件的事件源
5. 在 ES6 中類沒有變量提升,所以必須先定義類,才能通過類實例化對象
​
*/

tab欄切換案例

<style>
        * {
            margin: 0;
            padding: 0;
        }
​
        .content {
            width: 50%;
            height: 400px;
            border: 1px solid #000;
            margin: 100px auto;
            position: relative;
        }
​
        .content .top {
            height: 70px;
            border-bottom: 1px solid green;
​
​
        }
​
        .content .top ul {
            overflow: hidden;
        }
​
        .content .top ul li {
            position: relative;
            float: left;
            list-style: none;
            width: 60px;
            text-align: center;
            line-height: 70px;
            border-right: 1px solid red;
​
        }
​
        .content .top ul li i {
            background-color: blue;
            position: absolute;
            right: 0;
            top: 0;
            line-height: normal;
            font-style: normal;
            cursor: pointer;
        }
​
        .content .top .liactive {
            background-color: pink;
​
        }
​
        .add {
            position: absolute;
            top: 25px;
            right: 0;
​
        }
​
        .content .bottom>div {
            display: none;
        }
​
        .content .bottom>div.dactive {
            display: block;
        }
    </style>
</head>
​
<body>
    <!-- 大盒子 -->
    <div class="content" id="content">
        <!-- 上面的標題部分 -->
        <div class="top">
            <ul>
                <li class="liactive"><span>1標籤</span><i>x</i></li>
                <li><span>1標籤</span><i>x</i></li>
                <li><span>1標籤</span><i>x</i></li>
            </ul>
            <button class="add">+</button>
        </div>
        <!-- 下面的內容 -->
        <div class="bottom">
            <div class="dactive">1內容</div>
            <div>2內容</div>
            <div>3內容</div>
        </div>
    </div>
    <script>
        // 創建一個類,存放公共的方法
        class Tab {
            constructor(id) {
                this.main = document.querySelector(id);
                this.init();
            }
            init() {
                // 獲取標題的大盒子
                this.titleBox = this.main.querySelector('.top ul');
                // 獲取標題的列表
                this.titleList = this.main.querySelectorAll('.top li');
                // 獲取下面存放內容的大盒子
                this.contentBox = this.main.querySelector('.bottom ');
                // 獲取下面存放內容的列表
                this.contentList = this.main.querySelectorAll('.bottom>div');
                // 添加按鈕
                this.addBtn = this.main.querySelector('.add');
                // 刪除按鈕
                this.removeBtn = this.main.querySelectorAll('li i')
                this.toggle();
                this.add();
                this.remove();
                this.revise();
            }
            // 切換
            toggle() {
                var that = this;
                // 排他思想,點擊事件
                for (var i = 0; i < this.titleList.length; i++) {
                    // 設置自定義屬性
                    this.titleList[i].setAttribute('index', i);
                    // 點擊事件開始
                    this.titleList[i].onclick = function () {
                        for (var j = 0; j < that.titleList.length; j++) {
                            // 點擊的時候先清空類名,確定點擊哪一個的時候把類名給附給哪一個
                            that.titleList[j].className = '';
                            that.contentList[j].className = '';
                        }
                        // 點擊的標籤添加類名
                        this.className = 'liactive';
                        // 定義一個index 然後獲取到點擊的索引值
                        var index = this.getAttribute('index');
                        // 內容列表相同的下標的頁面添加相同的類名顯示出來
                        that.contentList[index].className = 'dactive';
​
                    }
                }
            }
            // 添加
            add() {
                var that = this;
                this.addBtn.onclick = function () {
                    // console.log('123');
                    var newli = '<li><span>新標籤</span><i>x</i></li>';
                    var newdiv = '<div>新內容</div>';
                    // console.log(this);//指向add
                    // console.log(that);//指向tab
                    that.titleBox.innerHTML += newli;
                    that.contentBox.innerHTML += newdiv;
                    that.init();
                    that.titleList[that.titleList.length - 1].click();
                }
            }
            // 刪除按鈕
            remove() {
                var that = this;
                for (var i = 0; i < this.removeBtn.length; i++) {
                    this.removeBtn[i].setAttribute('index', i);
                    this.removeBtn[i].onclick = function (e) {
                        e.stopPropagation();
                        // console.log('123');
                        // console.log(this);
                        // console.log(that);
                        var index = this.getAttribute('index');
                        that.titleList[index].remove();
                        that.contentList[index].remove();
                        // that.init();
                        if (index == 0) {
                            // return;
                            if (that.titleList.length == 1) {
                                return;
                            } else {
                                that.init();
                                that.titleList[0].click();
                            }
                        }
                        // 如果刪除以後發現選中的還在的話,就不需要切換
                        if (that.main.querySelectorAll('.liactive').length > 0) {
                            return;
                        }
                        index--;
                        that.titleList[index].click();
                    }
                }
​
​
            }
            // 修改標籤
            revise() {
                var that = this;
                for (var i = 0; i < this.titleList.length; i++) {
                    this.titleList[i].setAttribute('index', i);
                    this.titleList[i].ondblclick = function () {
                        // console.log('123');
                        // console.log(this);//指向的是li標籤                       
                        var newText = this.innerHTML;
                        this.innerHTML= `<input type="text" >`;
                        var input = this.children[0];
                        input.value = newText;
                        input.focus()
                        input.onblur = function () {
                            this.parentNode.innerHTML = this.value
                        }
                        console.log(input);
                        console.log(newText);
​
                    }
                }
                
            }
        }
        new Tab('#content');
    </script>
</body>

原型(原型鏈)

//構造函數 原型對象 實例對象三者的關係
/*
* 構造函數可以實例化對象
* 構造函數中有一個屬性叫prototype,是構造函數的原型對象
* 構造函數的原型對象(prototype)中有一個constructor構造器,這個構造器指向的就   是自己所在的原型對象所在的構造函數
* 構造函數的原型對象(prototype)中的方法是可以被實例對象直接訪問的
* 實例對象的原型對象(__proto__)指向的是該構造函數的原型對象
*/
/*
實例對象.__proto__ ===>構造函數的原型對象
實例對象.__proto__.__proto__ ===>Object的原型對象
實例對象.__proto__.__proto__.__proto__ ===>null
實例對象 = new 構造函數
構造函數.prototype ===>構造函數的原型對象
原型對象.constructor ===>構造函數
實例對象.constructor ===>構造函數
原型對象.__proto__ ===>Object的原型對象
原型對象.__proto__.constructor ===>Object
*/

this指向

/*構造函數中的this就是實例對象
原型對象中方法中的this就是實例對象*/
   function Mother(name, age) {
            this.name = name
            this.age = age
        }
        
        Mother.prototype.fn1 = function(){
            console.log(this);
        }
        var son = new Mother('小明', 20)
        son.fn1();

原型添加方法

不需要共享的數據寫在構造函數中,需要共享的數據寫在原型中(實例對象可以直接訪問原型對象中的屬性和方法)

原型的作用: 1. 數據共享, 節省內存空間 2. 實現繼承

/*構造函數裏面的方法會在每次new一個新對象的時候, 都存儲一次該方法, 這樣就會造成內存冗雜
解決1: 把這個方法sayHi寫成全局方法, 在構造函數中this.sayHi = sayHi;
這樣可能會造成函數名重複, 更麻煩
況且如果此時有10個方法需要存儲, 那麼需要在全局作用域裏面寫10個函數, 也比較佔內存
解決2: 原型 
每一個構造函數都有一個屬性   原型 / 也叫原型對象
可以給對象動態增加方法*/
function Student(name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
    } 
​
    Student.prototype.sayHi = function () {
      console.log('大家好,我是' + this.name);
    }
​
 //通過Student構造函數,創建的對象,可以訪問Student.prototype中的成員
    var s1 = new Student('lilei', 18, '男');
    var s2 = new Student('hmm', 18, '女');
    s1.sayHi();
    s2.sayHi();
/*當調用對象的屬性或者方法的時候,先去找對象本身的屬性/方法 ,如果對象沒有該屬性或者方法。此時去調用原型中的屬性/方法
如果對象本身沒有該屬性/方法,原型中也沒有該屬性或者方法,此時會報錯*/
​
/*
1. 對象的__proto__ 等於  構造函數的Student.prototype
2. s1.__proto__ === Student.prototype   //true
3. __proto__屬性是非標準的屬性->生產環境不能使用
4. 在原型對象中有一個屬性 constructor  構造函數
5. constructor  作用記錄了創建該對象的構造函數  記錄了創建該對象的構造函數
6. s1.constructor === Student   //true
7. 實例對象可以直接訪問原型對象中的屬性和方法
*/
//如果需要增加的方法多的話, 都寫給prototype就太複雜了可寫成
Student.prototype = {
      sayHi: function () {
        console.log('sayHi');
      },
      eat: function () {
        console.log('eat');
      }
    } 
​
//可是這樣的話, Student.prototype就是一個新的對象了, 不能指回Student.prototype的構造函數, 所以必須修改爲:
    Student.prototype = {
      constructor: Student,
      sayHi: function () {
        console.log('sayHi');
      },
      eat: function () {
        console.log('eat');
      }
    }
/*
   // test屬性在原型對象上,而在設置屬性的值的時候,不會搜索原型鏈
   // 而是直接給對象新增一個test屬性
   s1.test = '123xxx';
   此時s1自身會增加一個test屬性, 並且賦值爲123xxx
   而原型鏈裏面也有一個test屬性, 只不過被s1自身的test屬性覆蓋掉了
   所以此時輸出s2的test屬性的話, 值仍然是原來的值
*/

借用構造函數繼承

function Father(name, age) {
            this.name = name
            this.age = age
        }
        Father.prototype.fn = function () {
            console.log(123);
        }
​
        function Son(name, age) {
            Father.call(this, name, age)
        }
        // Son.prototype = Father.prototype;  這樣直接賦值會有問題,如果修改了子原型對象,父原型對象也會跟着一起變化(父類的原型對象會有子類原型對象特有的方法exam,這就是問題)
        // 通過指定父類實例對象方法解決:
        Son.prototype = new Father();
        // 如果利用對象的形式修改了原型對象,別忘了利用constructor 指回原來的構造函數
        Son.prototype.constructor = Son;
        var xm = new Son('小明', 20)
        console.log(xm);
        xm.fn()

this指向問題

調用方法 this指向
普通函數 window
構造函數 實例對象,原型對象裏面的方法也指向實例對象
對象方法調用 該方法所屬對象
事件綁定方法 綁定事件對象
定時器函數 window
立即執行函數 window
箭頭函數 window
//普通函數
function fn1(){
    console.log(this)//window
}
//window.fn1()
fn1()
​
//構造函數
function Person(){
    console.log(this)
}
var xm = new Person()
​
//對象方法調用
var obj = {
    sayHi:function(){
        console.log('對象方法的this:'+this);
    }
}
obj.sayHi();
​
//事件綁定方法
var btn = document.querySelector('button');
        btn.onclick = function() {
            console.log('綁定時間函數的this:' + this);
        };
​
// 5. 定時器函數 this 指向的也是window
        window.setTimeout(function() {
            console.log('定時器的this:' + this);
​
        }, 1000);
        // 6. 立即執行函數 this還是指向window
        (function() {
            console.log('立即執行函數的this' + this);
        })();
​
//箭頭函數
// 箭頭函數中不綁定this,箭頭函數中的this指向是它所定義的位置
 var uname = '張三'
 var obj = {
      uname :'李四',
      fn: function () {
      setTimeout(() => console.log(this.uname), 1000)//this指向李四
            }
        }
 obj.fn()

改變函數內部this指向

改變函數內this指向 js提供了三種方法 call() apply() bind()

//call()
var obj = {
            name: 'andy'
        }
        function fn(a, b) {
            console.log(a + b);
            console.log(this);
        }
        fn(); //此時的this指向的是window
        fn.call(obj, 2, 3); //此時的this指向的是對象obj
        // call 第一個可以調用函數 第二個可以改變函數內的this 指向
        // call 的主要作用可以實現繼承
        function Father(uname,age,sex){
            this.uname= uname;
            this.age = age
            this.sex = sex;
        }
        function Son(uname,age,sex){
           Father.call(this,uname,age,sex)
        }
        var son = new Son('小米',18,'男')
        console.log(son);
​
//apply()
   function fn(a, b) {
            console.log(a + b);
            console.log(this);
        }
        var o = {}
        fn() //此時的this指向的是window
        fn.apply(o, [2, 3]) //此時的this指向的是對象o,參數使用數組傳遞
        // 1. 也是調用函數 第二個可以改變函數內部的this指向
        // 2. 但是他的參數必須是數組(僞數組)
​
//bind()
// 1. 不會調用原來的函數   可以改變原來函數內部的this 指向
// 2. 返回的是原函數改變this之後產生的新函數
// 3. 如果有的函數我們不需要立即調用,但是又想改變這個函數內部的this指向此時用bind
        function fn() {
            console.log(this);
        }
        var o = {
            name: '測試'
        }
        // fn.bind(o)()相當於以下代碼
        var fn1 = fn.bind(o)
        fn1()

call、apply、bind三者的異同

  • 共同點 : 都可以改變this指向

  • 不同點:

    • call 和 apply 會調用函數, 並且改變函數內部this指向.

    • call 和 apply傳遞的參數不一樣,call傳遞參數使用逗號隔開,apply使用數組傳遞

    • bind 不會調用函數, 可以改變函數內部this指向.

  • 應用場景

    1. call 經常做繼承.

    2. apply經常跟數組有關係. 比如藉助於數學對象實現數組最大值最小值

    3. bind 不調用函數,但是還想改變this指向. 比如改變定時器內部的this指向.

數組的方法

//forEach 替代for循環,缺點不能提前中止
var arr = [1, 2, 3];
        var sum = 0;
        // forEach不能用break提前終止
        arr.forEach(function (value, index, array) {
            console.log(value); //數組每個元素
            console.log(index); //數組每個元素的索引
            console.log(array); //數組
            sum += value;
​
        })
        console.log(sum);
​
//map:映射 
var arr = [2, 3, 4]
var arr1 = arr.map(function (value) {
            return value * value;
        })
console.log(arr1);
​
//filter:過濾
 var arr = [12, 5, 36, 15, 21]
 var newArr = arr.filter(function (value, index, arrat) {
            // return value > 20
            return value % 2 == 0
        })
        console.log(newArr);
​
//some:判斷只要有一個滿足就是true
var arr = [12, 5, 36, 15, 21]
//返回值是布爾值,只要查找到滿足條件的一個元素就立馬終止循環
// 遍歷數組,判斷是否有滿足條件的元素,如果有返回true,如果沒有返回false
var bool = arr.some(function (value, index, arrat) { 
            return value % 2 == 0
        })
 console.log(bool);
​
//every:只有每一項滿足就是true
var arr = [12, 5, 36, 15, 21]
//返回值是布爾值,只要查找到滿足條件的一個元素就立馬終止循環
// 遍歷數組,判斷是否有滿足條件的元素,如果有返回true,如果沒有返回false
var bool = arr.every(function (value, index, arrat) {
      return value % 2 == 0
 })
 console.log(bool);
 
//reduce
 var arr = [2, 3, 4]
var sum = arr.reduce(function (a, b) {
        console.log(a,b);
        return a + b;
        })
console.log(sum);
​
//find
let arr = [{
            id: 1,
            name: '張三'
        }, {
            id: 2,
            name: '李四'
        }, {
            id: 3,
            name: '王五'
        }];
        var obj = arr.find(item => item.id == 2)
        console.log(obj);
        console.log(obj instanceof Object);
        //找數組裏面符合條件的值,當數組中元素id等於2的查找出來,注意,只會匹配第一個
        //要查找的條件,寫到箭頭函數的函數體中
        //find接收的箭頭函數的參數是固定的,item,屬性值
        //注意這裏的屬性值item是一個個的對象
        //obj是id爲2的item對象
​
//findIndex
 let arr = [{
            id: 1,
            name: '張三'
        }, {
            id: 2,
            name: '李四'
        }, {
            id: 3,
            name: '王五'
        }];
        var obj = arr.findIndex((item, index) => item.id == 2)
        console.log(obj); //1 返回的是索引號
​
//Array.from
var arr = {
            0: 1,
            1: 2,
            2: 3,
            length: 3
        }
        var arr1 = Array.from(arr); //轉化成真正的數組
        console.log(arr1);
        console.log(arr1 instanceof Array); //true
​
//擴展運算符...  將數組轉爲用逗號分隔的參數序列
 let ary = [1, 2, 3];
 ...ary  // 1, 2, 3
 console.log(...ary);    // 1 2 3,相當於下面的代碼
 console.log(1,2,3);
​
//擴展運算符可以應用於合併數組
// 方法一 
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
let ary3 = [...ary1, ...ary2];
// 方法二 
ary1.push(...ary2);
//方式三
//concat() 方法用於連接兩個或多個數組。
console.log(ary1.concat(ary2));
​
//將僞數組或可遍歷對象轉換爲真正的數組
let oDivs = document.getElementsByTagName('div'); 
oDivs = [...oDivs];

數組方法練習---商品案例

<style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
​
        .box {
            width: 500px;
            height: 400px;
            background-color: pink;
            border: 1px solid black;
            margin: 100px auto;
        }
​
        .bHead {
            width: 100%;
            height: 50px;
            text-align: center;
            border: 2px solid green;
        }
​
        .inText {
            width: 60px;
            height: 20px;
        }
​
        .bBody {
            width: 100%;
            height: 350px;
            text-align: center;
            border: 2px solid green;
        }
​
        .bBody table {
            width: 100%;
            height: 100%;
        }
    </style>
</head>
​
<body>
    <div class="box">
        <div class="bHead">
            價格:
            <input type="text" class="inText" id="star">-<input type="text" class="inText" id="end">
            <button id="pBtn">查詢</button>
            品牌:
            <input type="text" class="inText" id="bText">
            <button id="tBtn">查詢</button>
        </div>
        <div class="bBody">
            <table border="1" cellspacing=0>
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>類型</th>
                        <th>價格</th>
                    </tr>
                </thead>
                <tbody>
​
                </tbody>
            </table>
        </div>
    </div>
    <script>
        var arr = [{
                id: 1,
                name: '小米',
                price: 3099
            },
            {
                id: 2,
                name: 'oppo',
                price: 1999
            },
            {
                id: 3,
                name: '華爲',
                price: 3599
            }
        ]
        var tbody = document.querySelector('tbody');
        var star = document.querySelector('#star');
        var end = document.querySelector('#end');
        var pBtn = document.querySelector('#pBtn');
        var tBtn = document.querySelector('#tBtn');
        var inText = document.querySelector('#bText');
​
        function newArr(myArr) {
            tbody.innerHTML = ''
            myArr.forEach(function (value) {
                var tr = document.createElement('tr');
                tr.innerHTML = '<td>' + value.id + '</td><td>' + value.name + '</td><td>' + value.price +
                    '</td>';
                tbody.appendChild(tr)
            })
        }
        newArr(arr);
        pBtn.addEventListener('click', function () {
            // console.log('123');
​
            var arr1 = arr.filter(function (value) {
                // console.log(value);//返回的是arr數組中的對象
                return value.price >= star.value && value.price <= end.value;
​
            })
            console.log(arr1);
            newArr(arr1);
        })
        tBtn.addEventListener('click', function () {
            // console.log(inText)
            // console.log('123');
            var searchArr = [];
            arr.some(function (value) {
                // console.log(value.name, inText.value)
                if (value.name == inText.value) {
                    searchArr.push(value);
                    console.log(searchArr);
                }
                console.log(searchArr)
                newArr(searchArr);
            })
        })
    </script>
</body>
​

trim()方法去除空格

​
 <input type="text">
    <button>點擊</button>
    <script>
        var input = document.querySelector('input');
        var btn = document.querySelector('button');
        btn.addEventListener('click', function () {
            var str = input.value.trim();
            // console.log(str);
            if (str == '') {
                alert('請輸入內容')
            } else {
                console.log(str);
                console.log(str.length);
            }
        })
    </script>
​
//正則表達式去除空格
 // 自己實現一個trim
        String.prototype.trim1 = function () {
            return this.replace(/^\s+|\s$/g, "")
        }
        var str = '   123';
        var str1 = ' 456     ';
        console.log(str.trim1().length);

獲取對象的屬性名和數組的屬性值

Object.keys(對象)和Object.values(對象) 返回值是一個數組

var data = {
            id: 1,
            pname: '小米',
            price: 3999
        }
        var arr1 = Object.keys(data);
        var arr2 = Object.values(data);
        console.log(arr1);
        console.log(arr2);

修改或者設置對象中的屬性Object.defineProperty

Object.defineProperty(對象,修改或新增的屬性名,{
        value:修改或新增的屬性的值,
        writable:true/false,
       //如果值爲false 不允許修改這個屬性值
        enumerable: false,
        //enumerable 如果值爲false 則不允許遍歷
        configurable: false  //configurable 如果爲false 則不允許刪除這個屬性 屬性是否可以被刪除或是否可以再次修改特性
})  
// writable/enumerable/configurable的默認值都爲true.

函數的進階

高階函數對其他函數進行操作的函數,它接收函數作爲參數將函數作爲返回值輸出

//函數作爲參數    
function eat(fn) {
       setTimeout(function () {
         console.log('喫晚飯');
         // 喫完晚飯之後做的事情
         fn();
       }, 2000);
     }
​
     eat(function () {
       console.log('去唱歌');
     });
​
//函數作爲返回值輸出
// 第一次調用生成隨機數,以後每次調用都返回第一次的隨機值
​
      function getRandom() {
      var random = parseInt(Math.random() * 10) + 1;
      return function () {
      return random;
     }
      }
​
    var fn = getRandom();
​
     console.log(fn());
     console.log(fn());
     console.log(fn());
​

閉包 在一個作用域中可以訪問另一個作用域的變量

    function fn() {
      var n = 10;
      return function () {
        return n;
      }
    }
​
    var f = fn();
    console.log(f());
​
/*閉包特點:延展了函數的作用域範圍
fn返回值是一個函數, 這個函數的返回值是n, 因爲n將來還需要被使用, 所以fn運行完畢之後不會被系統銷燬, 而是等着將來提供n, 所以產生了閉包*/

遞歸 函數自己調用自己 遞歸,一般都要寫一個結束的條件

var count = 0;
//由於遞歸很容易發生“棧溢出”錯誤(stack overflow),所以必須要加退出條件return
        function fn() {
            console.log(123);
            count++;
            if (count == 6) {
                return; // 遞歸裏面必須加退出條件
            }
            fn(); //fn自己調用自己,fn就是遞歸函數
        }
        fn();
//遞歸求斐波那契數列
<script>
        function fn(n) {
            if (n === 1 || n === 2) {
                return 1
            }
            return fn(n - 1) + fn(n - 2)
        }
        console.log(fn(10));
</script>

拷貝(淺拷貝和深拷貝)

//淺拷貝
/*
淺拷貝, 對象中的基本數據類型拷貝沒問題, 但是如果對象中的複雜數據類型拷貝就有問題, 修改一個值, 都會跟着變化. 如下, 如果拷貝的obj1中修改了c的m, 那麼obj2的name會跟着變化
淺拷貝只拷貝對象的第一層屬性, 只拷貝了引用
*/
var obj1 = {
            a: 2,
            b: 3,
            c: {
                m: 1,
                n: 2
            }
        }
        // 思路一:用 for ... in 一個屬性,賦值給新的對象
        // var obj2 = {}
        // for (var key in obj1) {
        //     obj2[key] = obj1[key]
        // };
        // console.log(obj2);
        // 思路二
        var obj2 = Object.assign({}, obj1)
        obj2.a = 4;
        obj1.c.m = 5; //此時兩個對象都受影響
        console.log(obj1);
        console.log(obj2);
    </script>
​
//深拷貝
/*
必須先判斷是否是組數, 再判斷是否是對象, 因爲數組也是對象, 所以反過來的話, 會把所有的數組也當成對象來處理了
*/
var obj = {
            a: 2,
            b: 3,
            c: {
                m: 1,
                n: 2
            },
            d: [2, 3, 4, 5, 6]
        }
var o = {}
function deepCopy(newobj, oldobj) {
    for (var k in oldobj) {
    if (oldobj[k] instanceof Array) {
         newobj[k] = deepCopy([], oldobj[k])
            } else if (oldobj[k] instanceof Object) {
               newobj[k] = deepCopy({}, oldobj[k])
            } else {
                 newobj[k] = oldobj[k]
                }
            };
             return newobj;
        }
        deepCopy(o, obj);
        console.log(o);
        console.log(obj);
​

正則表達式 用於匹配字符串中字符組合的模式

邊界符

邊界符 說明
^ 表示匹配行首的文本(以誰開始)
$ 表示匹配行尾的文本(以誰結束)

如果 ^和 $ 在一起,表示必須是精確匹配。

var rg = /abc/; // 正則表達式裏面不需要加引號 不管是數字型還是字符串型
// /abc/ 只要包含有abc這個字符串返回的都是true
console.log(rg.test('abc'));//true
console.log(rg.test('abcd'));//true
console.log(rg.test('aabcd'));//true
console.log('---------------------------');
var reg = /^abc/;
console.log(reg.test('abc')); // true
console.log(reg.test('abcd')); // true
console.log(reg.test('aabcd')); // false
console.log('---------------------------');
var reg1 = /^abc$/; // 精確匹配 要求必須是 abc字符串才符合規範
console.log(reg1.test('abc')); // true
console.log(reg1.test('abcd')); // false
console.log(reg1.test('aabcd')); // false
console.log(reg1.test('abcabc')); // false

字符類

[]表示有一系列字符可供選擇,只要匹配其中一個就可以了

 // 中括號外邊的^爲邊界符,代表從什麼開始
        var reg = /^[a,b,c]$/
        console.log(reg.test('a'));
        //中括號裏面的^意思爲取反
        var reg1 = /^[^a,b,c]$/
        console.log(reg1.test('a'));

量詞符

 

量詞 說明
* 重複0次或更多次(*,理解爲任意)(0,1,n)
+ 重複1次或更多次(+:代表大於等於1的數。以0爲分界線,左邊是負數-,右邊是正數+)
? 重複0次或1次 (?問號,理解爲有沒有)
{n} 重複n次
{n,} 重複n次或更多次(沒有第二個數字,那就意味着是?,沒有上界)
{n,m} 重複n到m次 ({}:區間符號,次數區間)
// 1次或者多次 >=1
        var reg1 = /^a+$/
        // 0次或者多次 >=0
        var reg2 = /^a*$/
        // 0次或者1次 0||1
        var reg3 = /^a?$/
        // 3次
        var reg4 = /^a{3}$/
        // 3-5次
        var reg5 = /^a{3,5}$/
        // 3次以上
        var reg6 = /^a{3,}$/
        // 只能是數字 字母 下劃線組成
        var reg7 = /^[a-zA-Z0-9_]*$/
        // 只能是數字 字母 下劃線組成,長度6-18
        var reg8 = /^[a-zA-Z0-9_]{6,18}$/

括號總結

1.大括號 量詞符. 裏面表示重複次數

2.中括號 字符集合。匹配方括號中的任意字符.

3.小括號表示優先級

預定義類

理解:

d:digit ,數字

w:word,單詞

s:space,空白,間隔

預定義類 含義
/d 0-9任一數字,相當於[0-9]
/D 0-9數字以外的任意字符,相當於0-9
/w 任意的數字字母以及下劃線 [0-9a-zA-Z_]
/W 除去數字字母以及下劃線的字符 0-9a-zA-Z_
/s 匹配空格 (回車 tab 空格) 相當於[\t\r\n\v\f]
/S 匹配非空格 字符相當於[6\t\r\n\v\f]

正則替換replace

replace() 方法可以實現替換字符串操作,用來替換的參數可以是一個字符串或是一個正則表達式。

//過濾敏感詞彙案例
<textarea name="" id="message"></textarea> <button>提交</button>
<div></div>
<script>
    var text = document.querySelector('textarea');
    var btn = document.querySelector('button');
    var div = document.querySelector('div');
    btn.onclick = function() {
        div.innerHTML = text.value.replace(/激情|gay/g, '**');
    }
</script>

let

特點: 1. let聲明的變量只存在於塊級作用域 2.不存在變量提升 3.存在暫時性死區

//塊級作用域
 if (true) { // 大括號可以形成塊級作用域
     let a = 10;
 }
console.log(a) //報錯: a is not defined
​
//無變量提升
console.log(a); // a is not defined 
let a = 20;
​
//暫時性死區  利用let聲明的變量會綁定在這個塊級作用域,不會受外界的影響
 var tmp = 123;
 if (true) { //因爲在此塊級作用域中,使用了let聲明瞭tmp變量,那麼tmp會與此塊級作用域進行綁定,形成死區,不受外界影響(無法訪問外部的tmp)
     tmp = 'abc';//這裏會報錯,變量未聲明,不能賦值
     let tmp; 
 } 

let經典面試題

此題的關鍵點在於變量i是全局的,函數執行時輸出的都是全局作用域下的i值。
//沒有用到let
var arr = [];
 for (var i = 0; i < 2; i++) {
     arr[i] = function () {
         console.log(i); 
     }
 }
 arr[0]();//2
 arr[1]();//2
​
​
//用到let的時候
 let arr = [];
 for (let i = 0; i < 2; i++) {
     arr[i] = function () {
         console.log(i); 
     }
 }
 arr[0]();//0
 arr[1]();//1
/*
- 此題的關鍵點在於每次循環都會產生一個塊級作用域,
- 每個塊級作用域中的變量都是不同的,
- 函數執行時輸出的是自己上一級(循環產生的塊級作用域)作用域下的i值.
*/

小結

  • let關鍵字就是用來聲明變量的

  • 使用let關鍵字聲明的變量具有塊級作用域

  • 在一個大括號中 使用let關鍵字聲明的變量才具有塊級作用域 var關鍵字是不具備這個特點的

  • 防止循環變量變成全局變量

    • 循環中的i,使用var聲明的話是全局變量,但是這樣不合理,而使用let就是局部變量

  • 使用let關鍵字聲明的變量沒有變量提升

  • 使用let關鍵字聲明的變量具有暫時性死區特性

    • 死區:不受外界影響

const 聲明常量,常量就是值(內存地址)不能變化的量

常量:常態的量,值是常態的量,值無法改變

(const:constant常量)

1.存在塊級作用域 2.聲明的常量必須賦值,且賦值後無法修改 3.不存在變量提升

//存在塊級作用域
 if (true) { 
     const a = 10;
 }
console.log(a) // a is not defined
​
//聲明的常量必須賦值,且賦值後無法修改
const PI = 3.14;
PI = 100; // Assignment to constant variable. //給常量重新指定值
​
const ary = [100, 200];
ary[0] = 'a';//複雜數據類型內部的值,是可以改變的
ary[1] = 'b';
console.log(ary); // ['a', 'b']; 
ary = ['a', 'b']; // Assignment to constant variable.//但是複雜數據類型變量本身無法改變

小結

  • const聲明的變量是一個常量

  • 既然是常量不能重新進行賦值,如果是基本數據類型,不能更改值,如果是複雜數據類型,不能更改地址值

    • 複雜數據類型變量中存儲的是地址值

  • 聲明 const時候必須要給定初始值(因爲沒有辦法重新賦值,所以在聲明時必須有初始值

    var let const
    函數級作用域 塊級作用域 塊級作用域
    變量提升 不存在變量提升 不存在變量提升
    值可更改 值可更改 值不可更改

    解構賦值 可以讓我們更快捷的從數組或對象中提取值,然後對變量賦值

    //數組
     let [a, b, c] = [1, 2, 3];
     console.log(a)//1
     console.log(b)//2
     console.log(c)//3
    //如果解構不成功,變量的值爲undefined
    ​
    ​
    //對象
    let { name, age } = { name: 'zhangsan', age: 20 };
    console.log(name); // 'zhangsan' 
    console.log(age); // 20
    ​
    //注意這裏的name僅僅是用於屬性匹配,不在是變量,myName纔是變量
    let {name: myName, age: myAge} = { name: 'zhangsan', age: 20 };
    console.log(myName); // 'zhangsan' 
    console.log(name);//無法獲取到zhangsan
    console.log(myAge); // 20
    //如果變量不想與對象的屬性名保持一致,那麼採用第二種方式解構

    小結

    • 解構賦值就是把數據結構分解,然後給變量進行賦值

    • 如果解構不成功,變量跟數值個數不匹配的時候,變量的值爲undefined

    • 數組解構用中括號包裹,多個變量用逗號隔開,對象解構用花括號包裹,多個變量用逗號隔開

    • 利用解構賦值能夠讓我們方便的去取對象中的屬性跟方法

    • 補充:

      //不管是數組還是對象在去處理結構賦值時,都是通過key匹配變量,然後進行賦值.
       let [a, b, c] = [0:1, 1:2, 2:3];
      let { name, age } = { name: 'zhangsan', age: 20 };

       

箭頭函數

  • 箭頭函數中不綁定this,箭頭函數中的this指向是它所定義的位置

    • 理解:箭頭函數不綁定this,箭頭函數沒有自己的this關鍵字,如果在箭頭函數中使用this,this關鍵字將指向箭頭函數定義位置中的this

    • 再理解:理解爲當前箭頭函數作用域中沒有this,要向上一級作用域查找

  • 箭頭函數的優點在於解決了this執行環境所造成的一些問題。

    • 比如:解決了匿名函數this指向的問題(匿名函數的執行環境具有全局性),包括setTimeout和setInterval中使用this所造成的問題

//語法
/*函數體中只有一句代碼,且代碼的執行結果就是返回值,可以省略大括號
如果形參只有一個,可以省略小括號
箭頭函數不綁定this關鍵字,箭頭函數中的this,指向的是函數定義位置的上下文this
*/
() => {} //():代表是函數; =>:代表指向,指向哪一個代碼塊;{}:代碼塊,函數體
const fn = () => {}//代表把一個函數賦值給fn
​
//示例
const obj = { name: '張三'} 
 function fn () { 
     console.log(this);//this 指向 是obj對象
     return () => { 
         console.log(this);
         //this 指向 的是箭頭函數定義區域的this,那麼這個箭頭函數定義在fn裏面,而這個fn指向是的obj對象,所以這個this也指向是obj對象
         //理解爲當前箭頭函數作用域中沒有this,要向上一級作用域查找
     } 
 } 
 const resFn = fn.call(obj); 
 resFn();
​

string擴展方法

模板字符串中可以寫代碼,html代碼(可以識別空格和換行等格式),js代碼(寫在${})

1.模板字符串中可以解析變量 2.模板字符串中可以換行 3.在模板字符串中可以調用函數 4.同樣是通過${}來調用

​
let obj = {
            uname: '張三',
            age: 18
        }
////模板字符串中,通過${變量名}的方式來獲取變量值
var str = `你的名字是${obj.uname},你的年齡是${obj.age},${Math.random()}`
 console.log(str);//你的名字是張三,你的年齡是18,0.8866876958290935(隨機數)
var str1 = `<div> <h3>${obj.uname}</h3></div>`
console.log(str1);//<div> <h3>張三</h3></div>

實例方法

startsWith():表示參數字符串是否在原字符串的頭部,返回布爾值

endsWith():表示參數字符串是否在原字符串的尾部,返回布爾值

var str = 'abc.jpg';
        var reg = /jpg$/
        if (reg.test(str)) {
            console.log(1);
        } else {
            console.log(2);
        }
        console.log(str.endsWith('jpg')); //true 以什麼結尾
        console.log(str.startsWith('abc')); //true 以什麼開頭

repeat():表示將原字符串重複n次,返回一個新字符串

'x'.repeat(3)      // "xxx" 
'hello'.repeat(2)  // "hellohello"

set數據結構

實例方法:

  • add(value):添加某個值,返回 Set 結構本身

  • delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功

  • has(value):返回一個布爾值,表示該值是否爲 Set 的成員

  • clear():清除所有成員,沒有返回值

     const s = new Set();
     s.add(1).add(2).add(3); // 向 set 結構中添加值 
     s.delete(2)             // 刪除 set 結構中的2值   
     s.has(1)                // 表示 set 結構中是否有1這個值 返回布爾值 
     s.clear()               // 清除 set 結構中的所有值
     //注意:刪除的是元素的值,不是代表的索引

    遍歷

    Set 結構的實例與數組一樣,也擁有forEach方法,用於對每個成員執行某種操作,沒有返回值。

     const s = new Set();
     s.add(1).add(2).add(3); 
    s.forEach(value => console.log(value))

     

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章