進階教程 1. 閉包、this、和麪向對象

這是我參與8月更文挑戰的第8天,活動詳情查看:8月更文挑戰javascript

1、 閉包

閉包定義:

  • 解釋:函數執行時造成一個私有做用域,保護裏面的變量不受外界干擾,這種保護機制成爲閉包。
  • 市面理解:造成一個不銷燬的私有做用域(私有棧內存)纔是閉包

閉包應用:

1. 柯里化函數

柯里化函數思想:把多參數的函數變成單參數的函數java

function fn(a, b, c) {
   return a + b + c;
}

function fn1(a) {
   return function (b) { // 這種在函數中return 函數的作法是市面中認爲的閉包
      return function (c) {
         return a + b + c;
      }
   }
}
fn1(1)(2)(3);
複製代碼

2. 利用閉包機制隔離全局命名空間

(function () {
   // 自執行函數執行也是閉包
   let a = 100; // a是一個私有變量,不會影響全局做用域中的變量命名
})();

複製代碼

3. 惰性封裝

var utils = (function () {
   var version = '1.0.1';
   function sum(a, b) {
      return a + b
   }
   function minus(a, b) {
      return a - b;
   }
   return {
      sum: sum,
      minus: minus
   }
})();
複製代碼

4. 利用閉包的不銷燬做用域保存數據:累加計數、選項卡閉包版本

2、 this 關鍵字

this是JS的關鍵字,表明當前代碼執行的環境對象。通常在函數中使用,而且是在函數執行時,根據函數的不一樣執行方式肯定不一樣的值。目前階段有如下狀況:數組

1. 事件函數中的this是綁定該事件的元素;

let box = document.getElementById('box');
box.onclick = function () {
   console.log(this); // box 元素對象
};
複製代碼

2. 自執行函數中的this是window

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

複製代碼

3. setTimeout/setInterval() 定時器回調函數中的this指向window

setTimeout(function () {
   console.log(this);
}, 0); // 定時器寫0也不會馬上執行,也須要等待其餘同步代碼執行完纔會執行;

複製代碼

4.方法調用時,看方法前面是否有點 . 若是有點前面是誰,this就是誰,若是沒方法中的this就是window

var num = 13;
var obj = {
   num: 12,
   fn: function () {
      console.log(this.num);
   }
};
obj.fn(); // 12
obj['fn'](); // 12 obj['fn'] 等效於 obj.fn 因此,this仍然指向obj

var fn = obj.fn;
fn(); // 13;window.num

複製代碼

6. 箭頭函數中的this指向函數定義時所在做用域中的this

箭頭函數:

ES6新增的語法:省略function關鍵字,在形參入口後增長 => 箭頭,後面緊跟函數體;瀏覽器

let f = (a, b) => {
   return a + b;
   console.log(this)
};
f();

複製代碼

箭頭函數的簡化語法:

1. 只有一個形參時,能夠省略 形參入口的小括號
let f2 = a => {
   var x = 10;
   x += a;
   return x;
};
複製代碼

2. 若是函數只有一行代碼,或者只有return 指定返回值,能夠省略函數體的花括號和return關鍵字

let transfer = (a, b) => a + b;
// 等效於:
let transfer = function (a, b) {
   return a + b;
}

複製代碼

7. 全局做用域中的this是window

console.log(this);markdown

8. this在運行時不能夠賦值

this = {};  // 報錯
複製代碼

3、 ++i 和 i++

++i和i++都是給i累加1,可是加的時機不一樣閉包

  • ++i 是先累加自身,而後再取累加後的值和其餘值運算
  • i++ 是先取當前值和其餘值運算,再累加自身
var i = 0;
console.log(++i); // 1
console.log(i++); // 0

複製代碼
var a = 12; // 13 14
var b = 13; // 14 15

console.log(++a + a++ + b++ + ++b + a + b); // 13 + 13 + 13 + 15 + 14 + 15 = 83
複製代碼

4、 面向對象

4.1 面向過程

面向過程:以過程爲核心,研究如今要解決的問題,既不考慮之前,也不考慮未來,這段代碼就解決如今的問題。若是之後再有相同的功能,就再寫一遍相同的代碼;函數

4.2 面向對象

面向對象:是一種對現實世界的理解和抽象的方法。面向對象關心如今的功能能分分類解決,如今解決過的問題,咱們過往有沒有相似的代碼能夠複用,咱們如今的寫的代碼能不能給未來用。工具

4.3 面向對象的研究範疇

  • 對象:萬事萬物都是對象,每一個對象都具有各自的屬性和功能;post

  • 類:抽象事物的特性和特色,把對象分紅不一樣的類型,例如老師類、學員類,類是一個是描述一羣事物的共同特色的抽象概念;ui

  • 實例:類中的一個具體的個體。這個個體既然屬於類,那麼這個個體必定具備這個類的全部的特性和功能。

    1. 例如:人類就是一個類,人類最顯著的特色是能夠製造並使用工具。人類的屬性有語言、智慧、吃飯、睡覺.... 每一個人都是人類的一個實例,每一個人都有這個類型中的全部的特性和屬性。
  • ? 那麼js的面向對象體如今哪裏呢?

咱們強調數據類型,其中強調的就是類型。例如數組類、普通對象類,這是由於數組類和對象的屬性和方法不一樣。 例如數組是有序的鍵值對,還能夠push,pop、splice等,而對象是無序的鍵值對之和,並且對象不能夠pop 而 var ary = [1, 2] 是數組的一個實例,因此ary有全部數組的特性 而 var obj = {name: 1} 是對象的一個實例;

4.4 JS中的內置類:

  • Object
  • Array
  • Date
  • RegExp
  • Function

這些內置類都是函數數據類型的

console.log(typeof Array); // function
console.log(typeof Object); // function
複製代碼

面向對象的研究範疇

對於面向對象要求咱們掌握 封裝、類的繼承和多態

5、 單例模式

5.1 普通單例

在過往咱們面向過程時,描述一個同窗:

var name = '張三';
var age = 18;
var sex = 'boy';

複製代碼

描述另外一個同窗:

var name = '李四';
var age = 19;
var sex = 'girl';
複製代碼

這樣作有一個問題,由於變量只能表明一個值,全局變量後面的會覆蓋前面的。致使前面的數據丟失。 爲了解決這個問題,咱們如今在描述一個事物,咱們可使用一個對象,由於對象是用來描述一個事物的,而對象的屬性就是定性描述這個對象的特徵,而屬性值是定量的描述事物的這個特徵。

var stu1 = {
   name: 'zhangsan',
   age: 18,
   sex: 'boy'
};

var stu2 = {
   name: '李四',
   age: 19,
   sex: 'girl'
};

複製代碼

像上面這樣,把描述一個事物的屬性放到一個對象內這種封裝方式稱爲單例模式。 單例模式的優勢是解決了全局變量互相覆蓋的問題,這樣stu1的name和stu2的name沒有關係,由於stu1和stu2是兩個不一樣的對象,此時stu1和stu2表明的對象叫作單例,而stu1和stu2這兩個變量名稱爲命名空間;

5.2 高級單例

高級單例:高級單例模式再也不是直接將一個對象賦值給命名空間,而是先執行一個自執行函數,在函數執行結束時返回一個對象;

var person = (function () {
   function eat(food) {
      console.log(`I like eating ${food}`)
   }
   function hobby(h) {
      console.log(`I like playing ${h}`)
   }
   var account = '$10000000';
   var name = '王老五';
   var age = 40;
   return {
      name: name,
      age: age,
      eat: eat,
      hobby: hobby
   }
})();

複製代碼
  • 這樣寫,有一個優點,咱們能夠在自執行函數的做用域中聲明變量和函數,這個做用域不會銷燬,咱們能夠在最後返回對象裏面選擇導出哪些變量和方法給外界使用,不導出的,外界拿不到;

單例模式雖然好用,可是有一個問題,有一個對象,咱們就須要寫一個這個對象,很繁瑣。

6、 工廠模式

如何批量生產對象?

function reg(name, age, sex) {
   var obj = {}; // 原材料
   obj.name = name; // 加工
   obj.age = age; // 加工
   obj.sex = sex; // 加工
   return obj; // 出廠
}

let s1 = reg('阿三', 19, 'boy');
let s2 = reg('李四', 18, 'girl');

console.log(s1 === s2); // false

複製代碼
  • 工廠模式:像上面這樣,把實現相同供的函數封裝成一個函數,當咱們須要建立一個實例的時候,咱們就執行這個函數便可,而且每一個對象都是單例;
  • 優點:高內聚,低耦合 提升了代碼的複用度

工廠模式雖然解決了批量生產的問題,可是咱們說面向對象還要有類的概念,可是這種方式生產的對象都是同一類,沒有分類。

  • 思考?內置的類型都有類型的概念?

js有兩種建立數據的方式,一種是字面量,另外一種是實例的方式,例如:

var obj = new Object();
obj.name = '李四';
obj.age = 19;
obj.sex = 'girl';
console.log(obj);

複製代碼

// ? 爲何js能夠經過new 操做符來生成實例?並且是有類型的?咱們的能夠不能夠呢?

var obj2 = new reg('li', 13, 'g'); console.log(obj2); // 一樣獲取了一個實例對象,可是沒有發現類型

7. 構造函數模式

前面的工廠模式已經能夠批量生產了,可是仍是沒有咱們所說的面向對象中類型的概念。爲了有類型的概念,咱們須要構造函數模式。

構造函數:構造函數也是一個函數,可是調用方式有別於工廠函數:

  1. 調用方式不一樣,構造函數只能經過new操做符調用;
  2. 工廠函數內須要手動建立對象實例,而構造函數模式不須要手動建立對象,在構造函數被new執行時,構造函數中的this自動和實例綁定,因此咱們全部的加工都發生在this上;
  3. 工廠函數須要手動返回對象實例,而構造函數在被new操做符調用時不須要手動返回實例;

7、構造函數模式:

經過new調用一個函數,此時這個函數再也不是普通函數,而是成爲一個類,函數名稱爲類名,而經過new調用,自動返回這個類的一個實例。在構造函數中,咱們須要抽象這個類型的屬性和功能;

function Teacher(name, age, subject, from) {
   this.name = name;
   this.mission = '傳道受業解惑';
   this.age = age;
   this.subject = subject;
   this.from = from;
   this.teach = function () {
      console.log(`${name} 老師教 ${subject} 學科`);
   }
} // Teacher是一個類,這裏類型抽象了老師的屬性,一個老師有的屬性有姓名,年齡,教授學科,哪一個學校的老師,以及老師會講課的功能。

// 建立一個實例:
let mrMa = new Teacher('馬賓', 18, 'js', '江外琉璃');
console.log(mrMa);

let mrJiang = new Teacher('陸輝', 19, 'Architect-FE師', '江外琉璃');
console.log(mrJiang);
console.log(typeof mrJiang); // object
console.log(typeof mrMa); // object

複製代碼
  • 經過瀏覽器控制檯查看,這兩個實例(對象)的前面出現了 Teacher,此時說明mrMa和mrJiang都屬於Teacher類的實例。

instanceof 運算符

  • 如何檢測當前對象是不是當前類型的實例:

instanceof 運算符:檢測對象是不是某個類型的實例,若是是true,不然返回false

console.log(mrJiang instanceof Teacher);
console.log(mrMa instanceof Teacher);
console.log([] instanceof Teacher);

console.log([] instanceof Object); //
console.log(mrJiang instanceof Object);
console.log(mrMa instanceof Object);

複製代碼
  • 可是由於Object是基類,全部實例都是對象數據類型的,因此用instanceof檢測是不是Object的實例,都會返回true;
相關文章
相關標籤/搜索