深入理解JavaScript高級

JavaScript進階

變量類型

tyoeof:

typeof undefined  //undefined
typeof 'abc' //string
typeof 123 //number
typeof true //boolean
typeof {} //object
typeof [] //object
typeof null //object
typeof console.log //function

==:

100='100' //true
0 == '' //true
null == undefined //true

使用==||===:

推薦:出了下面這個其他地方全部用三等

if(obj.a==null){//相當於obj.a===null || obj===undefined這個的簡寫形式
	
}

內置函數有:

Object Array Boolean Number String Function Date RegExp Error Math

創建對象:

  1. 創建一個新對象
  2. this指向新對象
  3. 執行代碼,賦值等運算
  4. 返回this
  5. 如果構造函數返回一個對象,那麼這個對象會取代整個new出來的結果。那麼這個對象會取代整個new出來的結果。如果構造函數沒有返回對象,那麼new出來的結果爲步驟1創建的對象
1、字面量創建

全爲靜態成員,適用於tools類等

var MyMath = {
      PI: 3.1415926,
      max: function () {  },
      min: function () {  }
    } 
console.log(MyMath.PI);
2、工廠方法創建對象

創建對象爲Object的對象,而不是一個hero的對象

//  工廠函數創建對象 
    function createHero(name, blood, weapon) {  
      var o = new Object();
      o.name = name;
      o.blood = blood;
      o.weapon = weapon;
      o.attack = function () {  
        console.log(this.weapon+'攻擊敵人');
      }
      return o;
    }

	var hero = createHero('劉備',100,'劍');
3、構造函數

全爲實例成員,創建的對象爲hero,但多個對象會存儲多個同樣的attack方法,佔用代碼冗餘

function Hero(name, blood, weapon) { 
      this.name = name;
      this.blood = blood;
      this.weapon = weapon;
      this.attack = function () {  
        console.log(this.weapon+'攻擊敵人');
      }
    }

    var hero =new  Hero('劉備',100,'劍');

改1:

解決了存儲問題,但污染了全局作用域

function Hero(name, blood, weapon) {
      this.name = name;
      this.blood = blood;
      this.weapon = weapon;
      this.attack = attack;
    }

    function attack() {
      console.log(this.weapon + '攻擊敵人');
    }

    var hero = new Hero('劉備', 100, '劍');
4、使用原型

解決了存儲問題,同樣不污染全局作用域

function Hero(name, blood, weapon) {
      this.name = name;
      this.blood = blood;
      this.weapon = weapon;
    }
    Hero.prototype.attack = function () {  
      console.log(this.weapon + '攻擊敵人');
    }
  
    var hero = new Hero('劉備', 100, '劍');

簡單原型

在 JavaScript 中,每當定義一個對象(函數也是對象)時候,對象中都會包含一些預定義的屬性。其中每個函數對象都有一個prototype 屬性,這個屬性指向函數的原型對象。原型對象,顧名思義,它就是一個普通對象。

img
在這裏插入圖片描述

原型鏈

在這裏插入圖片描述

繼承

原型繼承:

無法設置構造函數的參數

    function Person() {
      this.name = 'zs';
      this.age = '18';
      this.sex = '男'
    }

    function Student() {
      this.score = '99';
    }
    Student.prototype = new Person();
    Student.prototype.constructor = Student;

    function Teacher() {
      this.salary = 3000;
    }

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-JBpBys6Y-1570539608298)(C:\Users\10592\AppData\Roaming\Typora\typora-user-images\1564197765564.png)]

借用構造函數

方法怎麼繼承呢?

// 借用構造函數
    function Person(name, age, sex) {  
      this.name = name;
      this.age = age;
      this.sex = sex;
    }

    function Student(name, age, sex,score) { 
      Person.call(this,name,age,sex);
      this.score = score;
    }
組合繼承:借用構造函數+原型繼承
function Person(name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
    }
    Person.prototype.sayHi = function () {
      console.log('大家好!我是' + this.name);
    }

    function Student(name, age, sex, score) {
      Person.call(this, name, age, sex);
      this.score = score;
    }
    // 讓字類型繼承父類型的方法
    Student.prototype = new Person();
    Student.prototype.constructor = Student;
    Student.prototype.exam = function () {
      console.log('考試');
    }

    function Student(name, age, sex, score) {
      Person.call(this, name, age, sex);
      this.score = score;
    }
    Teacher.prototype = new Teacher();
    Teacher.prototype.constructor = Teacher;
    Teacher.prototype.gaijuan = function () {
      console.log('改卷');
    }

函數

函數的定義方式

函數聲明與函數表達式
// 函數的聲明
    fn();
    function fn() {  
      console.log('test');
    } 
//報錯
// 函數表達式
    fn1();
    var fn1= function () {  
      console.log('test');
    }

區別:

  • 上面代碼由於聲明提前可看成:所以fn1()處報錯
//聲明提前
function fn() {  
      console.log('test');
 } 
 var fn1;
 
 fn();
 fn1();
 fn1= function () {  
      console.log('test');
 }
  • 在老版本的IE中 if 語句中的函數聲明會提前 但函數表達式並不會發生這樣的情況
// 瀏覽器問題
    // 根據條件聲明函數
    // 在現代瀏覽器 不會提升if語句中的函數聲明
    // 在老版本的IE中  if語句中的函數聲明會提前
    if(true){
      function fn() {
        console.log('test--true');
      }
    }else{
      function fn() {
        console.log('test--false');
      }
    }

運行結果:

現代瀏覽器:test--ture

IE8:test--false

new Function

不推薦使用,因爲執行速度慢(先解析字符串,在執行)

認識到函數也是對象

var fn = new Function('var name = "張山";console.log(name);')
    fn();
    console.dir(fn);

this

函數內部的this,是由函數調用的時候來確定其指向

普通函數調用

this指向window

// 1、普通函數
    function fn() {}
    fn();
   //相當於window.fn();
方法調用

this指向obj,指向調用該方法的對象

// 2、方法調用 this指向obj
    var obj={
      fn:function () {}
    }
    obj.fn();
構造函數調用

構造函數內部的this指向由構造函數創建的對象,this指向hero實例

// 3、構造函數調用  構造函數內部的this指向由構造函數創建的對象
function Hero(name, blood, weapon) {
      this.name = name;
      this.blood = blood;
      this.weapon = weapon;
    }
    Hero.prototype.attack = function () {  
      console.log(this.weapon + '攻擊敵人');
    }
  
    var hero = new Hero('劉備', 100, '劍');
    hero.attack();
作爲事件處理函數調用

作爲事件處理函數調用 this指向觸發該對象的對象,this指向btn

// 4、作爲事件處理函數調用  指向觸發該對象的對象
    btn.onclick=function(){
      console.log(this);
    }
作爲定時器的參數

作爲定時器的參數 this指向window

// 5、作爲定時器的參數  this指向window
    setInterval(function() {
      console.log(this);
    }, 1000);

函數中的方法

call的應用
  • 使僞數組使用數組的方法
  • 使arr數組可以使用ObjecttoString 而不是自己重寫的toString 可以找到數組對象的類型
// 僞數組
    var obj={
      0: 100,
      1: 60,
      2: 60,
      length: 3
    };

    Array.prototype.push.call(obj,30);
    Array.prototype.splice.call(obj,0,2);
    console.dir(obj);

    var arr = [5,9];
    console.log(arr.toString());
    console.log(Object.prototype.toString.call(arr));

最後三句輸出:

  • 5 ,9
  • object Array
apply的應用

可以把數組中的每一項展開

// apply
    var arrp = [6,10,5,9,3];
    
    console.log(Math.max.apply(Math,arr)); //輸出:10
    console.log(null,arr); //輸出:6 10 5 9 3
bind的應用
  • 改變定時器的this指向

  • 改變事件處理函數中的this指向

    改變定時器的this指向,讓this爲function中的this,而function的又是obj調用,所以this指向obj

 var obj={
      name:'zs',
      fun:function () {  
        setInterval(function() {
          console.log(this.name);  
        }.bind(this), interval);  //這裏的this是function的this,
      }
    }
    obj.fn();//輸出zs

    btn.onclick = function () {  

    }.bind(obj);
其他方法與arguments的應用

當函數參數不固定的時候,在函數內部可以通過arguments獲取

//其他方法
    function fn(x,y) {  
      // 函數內部有一個私有變量 arguments(它不是fn.arguments)但跟fn.arguments作用一樣(僞數組,獲取到的是函數的實參)
      console.log(arguments);
      console.log(fn.arguments);  //僞數組,獲取到的是函數的實參,可以根據arguments獲取實參
      console.log(fn.caller);     //函數的調用者,全局調用時爲null
      console.log(fn.name);       //函數的名稱,字符串類型
      console.log(fn.length);     //函數的形參個數
    }
    function test() {  
      fn(1,3,6);
    }

    test();
	//當函數參數不固定的時候,在函數內部可以通過arguments獲取
    function max() {  
      var max = arguments[0];
      for(var i=0;i<arguments.length;i++){
        if(max<arguments[i]){
          max=arguments[i];
        }
      }
      return max;
    }

    console.log(max(1,2,8,9,1,7,3));

高階函數

函數作爲參數與sort的內部實現
// 1、函數作爲參數
    function eat(fn) {  
      setTimeout(function() {
        // 喫晚飯
        console.log('喫晚飯');
        // 喫晚飯後的事情
        fn();
      }, 2000);
    }
    eat(function() {  
      console.log('去唱歌');
    });

    var arr=[30,55,14,61];
    arr.sort(function(a, b) {
      return a - b;
    })
    console.log(arr);
    
    // arr.mySort
    Array.prototype.mySort = function (fn) {  
      for(var i=0;i<this.length-1;i++){
        var isSort = true;
        for(var j=0;j<this.length-i-1;j++){
          if(fn(this[j],this[j+1])>0){
            isSort =false;
            var temp = this[j];
            this[j] = this[j+1];
            this[j+1] = temp;
          }
        }
        if(isSort){
          break;
        }
      }
    }

    var arr=[30,55,14,61];
    arr.mySort(function(a,b) {
      return a-b;
    });
    console.log(arr);
函數作爲返回值(可能發生閉包)
  • 寫一個函數,第一次調用生成1-10隨機數,以後的每一次調用都返回第一次的隨機值
  • 求兩個數的和n是變化的
    100+n
    1000+n
    10000+n
    第一個參數也會變化但卻是規定的
  • 兩個案例都發生了閉包
//寫一個函數,調用生成1-10隨機整數數
    function getRandom() {  
      return parseInt(Math.random()*10)+1;
    }
    console.log(getRandom());
    // 寫一個函數,第一次調用生成1-10隨機數,以後的每一次調用都返回第一次的隨機值
    function getOnceRandom() {  
      var random=parseInt(Math.random()*10)+1;
      return function() {  
        return random;
      }
    }
    var fn = getOnceRandom();
    console.log(fn());
    console.log(fn());
    console.log(fn());
    // 求兩個數的和
    // 100+n
    // 1000+n
    // 10000+n
    // 第一個參數也會變化但卻是規定的
    function getFun(m) {
      return function(n) {  
        return m+n;
      }
    }

    var fn100=getFun(100);
    var fn1000=getFun(1000);

    console.log(fn100(15)); //115
    console.log(fn1000(15)); //1015

閉包

在一個作用域中可以訪問另一個作用域的變量或者函數

上面兩個案例隨機數和求和都發生了閉包

//未發生閉包
    function fn1() {  
      var n=10;
      return n;
    }
    fn1();
    // 閉包
    // 特點:延展了函數作用域的範圍
    function fun() {  
      var n=10;
      return function() {  
        return n;
      }
    }
    var f=fun();
    console.log(f());

案例1:

輸出點擊元素的索引

方式一更好,因爲外部的自調用函數的作用域並沒有釋放性能變低,所以閉包不一定更好

// 案例1:輸出點擊元素的索引
    // 方式1:
    var heroes = document.getElementById('heroes');
    var list = heroes.children;
    for (var i = 0; i < list.length; i++) {
      var li = last[i];
      li.index = i;
      li.onclick = function () {
        console.log(this.index);
      }
    }
    // 方式2:使用閉包(第一種更好,因爲外部的自調用函數的作用域並沒有釋放,所以閉包不一定更好)
    var heroes = document.getElementById('heroes');
    var list = heroes.children;
    for (var i = 0; i < list.length; i++) {
      var li = last[i];
      (function (i) {
        li.onclick = function () {
          console.log(i);
        }
      })(i);
    }

setTimeout的原理

在這裏插入圖片描述

console.log('start');
    setTimeout(function() {
      console.log('timeout');
    }, 0);

    console.log('over');

輸出:start over timeout

案例2:

點擊按鈕改變字體大小

// 案例2:
    var btn01 = document.getElementById('btn01');
    var btn02 = document.getElementById('btn02');
    var btn03 = document.getElementById('btn03');

    // btn01.onclick = function () {
    //   document.body.style.fontSize = '12px';
    // }
    // btn02.onclick = function () {
    //   document.body.style.fontSize = '14px';
    // }
    // btn03.onclick = function () {
    //   document.body.style.fontSize = '16px';
    // }

    function makeFun(size) {  
      return function () {  
        document.body.style.fontSize = size +'px';
      }
    }

    btn01.onclick = makeFun(12);
    btn02.onclick = makeFun(14);
    btn03.onclick = makeFun(16);

思考1:

沒有發生閉包

輸出:The Window

var name = "The Window";
    var object={
      name:'My Obejct',
      getNameFun:function () {  
        return function () {  
          return this.name;
        }
      }
    }
    console.log(object.getNameFun()()); //The Window

思考2:

發生了閉包

輸出:My Obejct

// 思考2:
    var name = "The Window";
    var object = {
      name: 'My Obejct',
      getNameFun: function () {
        var that =this;
        return function () {
          return that.name;
        }
      }
    }
    console.log(object.getNameFun()()); //My Obejct

遞歸:

一般都要寫一個結束條件,不然會堆棧溢出

案列1:

遞歸實現1+2+3+4+…+n

// 遞歸實現1+2+3+4+...+n
    function getSum() {
      if (n===1) {
        return 1;
      }
      return n+getSum(n-1);
    }

案例2:

獲取斐波那契數列的第n個數
數列:1、1、2、3、5、8、13、21、34

// 斐波那契數列的第n個數
    // 數列:1、1、2、3、5、8、13、21、34
    
    function getFB(n) {  
      if(n===1||n===2){
        return 1;
      }
      return getFB(n-1)+getFB(n-2);
    }

淺拷貝:

改變拷貝前的對象,發現普通屬性沒有隨着拷貝前的對象的改變而變化,但是當屬性也是一個對象時,會發生改變

 // 對象的淺拷貝
    var obj1 = {
      name: 'zs',
      age: 18,
      sex: '男',
      dog: {
        name: '金毛',
        age: 5,
        yellow: '黃色'
      }
    }
    var obj2 = {}
    // 淺拷貝封裝
    function copyObject(o1, o2) {
      for (var key in o1) {
        o2[key] = o1[key];
      }
    }
    copyObject(obj1, obj2);

    obj1.name = 'xxxx';
    obj1.dog.name = '大黃';
    console.dir(obj2);

在這裏插入圖片描述

深拷貝:

不管屬性是什麼類型,改變拷貝前的對象都不會影響拷貝後的對象

// 深拷貝:
    var obj1 = {
      name: 'zs',
      age: 18,
      sex: '男',
      dog: {
        name: '金毛',
        age: 5,
        yellow: '黃色'
      }
    }
    var obj2 = {}
    // 深拷貝:
    function deepCopy(o1,o2) {  
      for (var key in o1) {
        var item = o1[key];
        if(item instanceof Object){
          o2[key]={};
          deepCopy(item,o2[key]);
        }else if (item instanceof Array){
          o2[key]=[];
          deepCopy(item,o2[key]);
        }else{
          o2[key] = o1[key];
        }
      }
    }
    deepCopy(obj1,obj2);

    obj1.name = 'xxxx';
    obj1.dog.name = '大黃';
    console.dir(obj2);
    console.log(obj1);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章