《你不知道的JS上》筆記

JS是編譯型語言

編譯發生在代碼執行前幾微秒,簡單來講就是js在執行前要進行編譯,編譯過程發生在代碼執行前幾微妙,甚至更短。閉包

編譯的步驟

  1. 詞法分析
    以var a = 2 爲例,詞法分析會將其分紅三個有意義的代碼塊即詞法單元。
  2. 語法分析
    將詞法單元組合生成表明了程序語法的結構的樹,即抽象語法書(AST)。
  3. 代碼生成
    將AST生成可執行的代碼。即將AST轉化成一組機器指令。​​​

LHS RHS

若是查找的目的是對變量進行賦值,那麼就會使用 LHS 查詢;若是目的是獲取變量的值,就會使用 RHS 查詢。app

詞法做用域

決定於你在寫代碼時的塊級做用域ide

優化

依賴於詞法的靜態分析函數

eval with 會建立新的做用域

在詞法分析階段,沒法知道eval with會對做用域作怎樣的修改,此時引擎再也不對做用域進行任何優化優化

函數做用域

函數聲明 函數表達式

區分函數聲明和表達式最簡單的方法是看 function 關鍵字出如今聲明中的位
置(不只僅是一行代碼,而是整個聲明中的位置)。若是 function 是聲明中
的第一個詞,那麼就是一個函數聲明,不然就是一個函數表達式。this

let

  1. 隱式的生成塊級做用域
  2. 不存在變量提高

提高

緣由

變量(包括函數在內)的全部聲明都會優先執行,只有聲明自己會提高,而賦值或其餘運行邏輯會留在原位置prototype

過程

這意味着不管做用域中的聲明出如今什麼地方,都將在代碼自己被執行前首先進行處理。
能夠將這個過程形象地想象成全部的聲明(變量和函數)都會被「移動」到各自做用域的
最頂端,這個過程被稱爲提高。
聲明自己會被提高,而包括函數表達式的賦值在內的賦值操做並不會提高。code

閉包

定義

當函數可以記住或訪問所在的詞法做用域,及時是被做用域外調用,就產生了閉包對象

模塊

  1. 現代模塊機制
  2. 將來的模塊機制

關於this

綁定時間點

是在函數運行時綁定的,而非定義時。它的上下文取決於函數調用時的各類條件,和在哪裏定義的沒有關係,只取決於函數的調用方式。繼承

綁定過程

當函數被調用時,會建立一個執行上下文,在這個上下文裏包含了函數在哪裏沒調用(調用棧),調用函數的方法,參數等。this做爲執行上下文的一個屬性,能夠在函數執行的過程當中用到。

綁定類型

  1. 默認綁定
    即綁定到全局,嚴格模式下回綁定到undefined。

    function foo() {
      console.log( this.a );
    }
    var a = 2;
    (function(){
      "use strict";
       foo(); // 2
    })()
  2. 隱式綁定
    即綁定到最頂層(或最近調用對象)上

    function fun() {
      console.log(this.a)
    }
    var obj2 = {
      a: 3,
     fun: fun,
    }
    var obj1 = {
      a: 2,
      obj2: obj2,
    }
    obj1.obj2.fun() // 3
  3. 顯式綁定
    即用call或apply手動進行綁定
  4. bind方法實現
  5. new綁定(構造函數)

    1. 不存在
      其實在js中不存在構造函數,咱們所說的構造函數其實就是普通的函數,它只是用new被「構造調用」而已。
    2. new發生了什麼?

      1. 建立(或者說構造)一個全新的對象。
      2. 這個新對象會被執行[[原型]]鏈接。
      3. 這個新對象會綁定到函數調用的this。
      4. 若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象。
  6. 箭頭函數 =>

對象

內置對象

基本類型在須要的時候(好比說獲取長度),會被引擎默認轉成內置對象,從而進行方法的調用。
基礎類型並非繼承自內置對象​

var strPrimitive = "I am a string";
    typeof strPrimitive; // "string"
    strPrimitive instanceof String; // false
    var strObject = new String( "I am a string" );
    typeof strObject; // "object"
    strObject instanceof String; // true
    Object.prototype.toString.call( strObject ); // [object String]

null

typeof null === Object;

原理是這樣的,不一樣的對象在底層都表示爲二進制,在 JavaScript 中二進制前三位都爲 0 的話會被判
斷爲 object 類型,null 的二進制表示是全 0,天然前三位也是 0,因此執行 typeof 時會返回「object」

拷貝

  1. 淺拷貝
    Object.assign({}, obj)
  2. 深拷貝
    JSON.stringify

屬性描述符

getOwnPropertyDescriptor(myObj, 'a')
defineProperty
Object.defineProperty(myObj, 'a', {
  value: 2,            
  ​writable: true,
  configurable: true, 
  enumerable: true 
​})

Getter 、Setter

var obj = {
  get a() {
    return this._a_
  },
  set a(val) {
   this._a_ = val * 5
  }
}
obj.a = 10
console.log(obj.a) // 50
​
var obj2 = {}
Object.defineProperty(obj2, 'a', {
  get: function() {
    return this._a_
  },
  set: function(val) {
    this._a_ = val * 2
  }
})
obj2.a = 15
console.log(obj2.a) // 30

存在性

  1. in
    'a' in obj1 會檢查obj及其原型鏈上是否有'a'
  2. hasOwnProperty
    不會檢查原型鏈,若是須要能夠Object.prototype.hasOwnProperty.call(myObj, 'a')

原型(prototype)

constructor

返回實例對象O的構造函數(的引用)。任何一個prototype對象都有一個constructor屬性,指向它的構造函數,每個實例也有一個constructor屬性,默認調用prototype對象的constructor屬性​
例如

function Test() {
  this.name = 'test'
}
var test = new Test()
console.log(test.constructor === Test) // true

類constructor

構造函數 constructor 是用於建立和初始化類中建立的一個對象的一種特殊方法.

class Polygon {
    constructor() {
        this.name = "Polygon";
    }
}
class Square extends Polygon {
    constructor() {
        super();
    }
}
class Rectangle {}
Object.setPrototypeOf(Square.prototype, Rectangle.prototype);
console.log(Object.getPrototypeOf(Square.prototype) === Polygon.prototype); //false
console.log(Object.getPrototypeOf(Square.prototype) === Rectangle.prototype); //true
let newInstance = new Square();
console.log(newInstance.name); //Polygon​

proto

實例對象__proto__指向生成改對象的構造函數的原型
例如

|function Test() {
  this.name = 'test'
}
Test.prototype = {
  color: 'red'
}
var test = new Test()
console.log(test.__proto__ === Test.prototype) // true
console.log(test.__proto__)

Object.create

var foo = {
something: function() {
  console.log( "Tell me something good..." );
}
};
var bar = Object.create( foo ); 
bar.something(); // Tell me something good...
Object.create(..) 會建立一個新對象(bar)並把它關聯到咱們指定的對象(foo)

這樣 咱們就能夠充分發揮 [[Prototype]]
機制的威力(委託)而且避免沒必要要的麻煩
(好比使 用 new 的構造函數調用會生成 .prototype 和 .constructor 引用)。

繼承

原型繼承

缺點
實例的屬性都會指向同一個引用
實現

function Parent() {
  this.names = [1, 2, 3]
}
function Child() {
  
}
Child.prototype = new Parent()
var child1 = new Child()
var child2 = new Child()
child1.names.push(4)
console.log(child1.names) // [1,2, 3,4]
console.log(child2.names) // [1,2, 3,4]

借用構造函數

實現

function Parent() {
  this.names = [1, 2, 3]
  this.getName = function () {
    console.log(this.name)
  }
}
function Child() {
  Parent.call(this)
}
var child1 = new Child()
var child2 = new Child()
child1.names.push(4)
console.log(child1.names)
console.log(child2.names)

缺點
每一個子實例都會實例化方法一次,內存爆炸

組合繼承(最經常使用)

實現

function Parent() {
  this.names = [1, 2, 3]
}
Parent.prototype.getName = function () {
  console.log(this.names)
}
function Child() {
  Parent.call(this)
}
Child.prototype = new Parent()
var child1 = new Child()
var child2 = new Child()
child1.names.push(4)
child1.getName()
child2.getName()

缺點

  1. 子類實例上有一份父類的屬性,兩者重複形成內存浪費
  2. 父類的構造函數被調用了兩次​

寄生式組合繼承

實現

function Parent() {
  this.names = [1, 2, 3]
}
Parent.prototype.getName = function () {
  console.log(this.names)
}
function Child() {
  Parent.call(this)
}
Child.prototype = Object.create(Parent.prototype)
var child1 = new Child()
var child2 = new Child()
child1.names.push(4)
child1.getName()
child2.getName()

優勢
屬性不會再原型鏈上重複

行爲委託

js中的繼承其實就是在對象間創建關聯關係,而行爲委託就是創建這種關聯關係的紐帶。

("原型")面向對象風格

function Foo(who) {
  this.me = who;
}
Foo.prototype.identify = function () {
  return "I am" + this.me;
};
function Bar(who) {
  Foo.call(this,who);
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.speak = function () {
  alert("Hello," + this.identify() + '.');
};
var b1 = new Bar("b1");
var b2 = new Bar("b2");
b1.speak();
b2.speak();

對象關聯風格

Foo = {
  init:function (who) {
    this.me = who;
  },
  identify:function () {
    return "I am" + this.name
  }
};
Bar = Object.create(Foo);
Bar.speak = function () {
  alert("hello," + this.identify() + '.');
};
var b3 = Object.create(Bar);
b3.init("b3");
var b4 = Object.create(Bar);
b4.init("b4");
b1.speak();
b2.speak();

相關文章
相關標籤/搜索