国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

深入理解JavaScript原型和閉包

missonce / 915人閱讀

摘要:本文是本人閱讀學(xué)習(xí)深入理解原型和閉包時所作的總結(jié)和筆記,當然也引用了很多原文,感興趣的朋友也可以直接去看原文。即這里的稱為隱式原型。注意,構(gòu)造函數(shù)的函數(shù)名第一個字母大寫規(guī)則約定。但實際上,上述情況是一種理想的情況。

本文是本人閱讀學(xué)習(xí)深入理解JavaScript原型和閉包時所作的總結(jié)和筆記,當然也引用了很多原文,感興趣的朋友也可以直接去看原文。

1、一切都是對象

先說結(jié)論,一切引用類型都是對象,對象是屬性的集合。

首先我們對不同變量使用typeof()看看都有哪些輸出的類型。

console.log(typeof(x));                 // undefined
console.log(typeof(10));                // number
console.log(typeof("abc"));             // string
console.log(typeof(true));              // boolean
console.log(typeof(function () {}));    // function
console.log(typeof([1, "a", true]));    // object
console.log(typeof({ a: 10, b: 20 })); // object
console.log(typeof(null));             // object
console.log(typeof(new Number(10)));   // object

在以上代碼中,undefined, number, string, boolean屬于值類型,不是對象。
而其他的幾種類型 - 包括函數(shù)、數(shù)組、對象、null、new Number(10)都是對象,它們屬于引用類型

在JavaScript中,數(shù)組是對象,函數(shù)是對象,對象還是對象。對象里面的一切都是屬性,只有屬性,沒有方法,或者說方法也是一種屬性。屬性表示為鍵值對的形式。

JavaScript中的對象可以任意的擴展屬性,定義屬性的方法通常有兩種。

var obj = {
  a = 10,
  b: function(x) {
    console.log(this.a + x)
  },
  c: {
    name: "Steven",
    year: 1988
  }
}

上面這段代碼中,obj是一個自定義的對象,其中a、bc是它的屬性,而屬性c的本身又是一個對象,它又有name、year兩個屬性。

函數(shù)和數(shù)組不能用上面的方法定義屬性,下面以函數(shù)為例:

var fn = function () {
  alert(100);
};
fn.a = 10;
fn.b = function () {
  alert(123);
};
fn.c = {
  name: "Steven",
  year: 1988
};

在jQuery源碼中,變量jQuery或者$其實是一個函數(shù),我們可以用typeof()驗證一下:

console.log(typeof ($));  // function
console.log($.trim(" ABC "));

很明顯,這就是在$或者jQuery函數(shù)上加了一個trim屬性,屬性值是函數(shù),作用是截取前后空格。

2、函數(shù)和對象的關(guān)系

上文已經(jīng)說到,函數(shù)也是一種對象,我們可以用instanceof驗證一下:

var fn = function () { };
console.log(fn instanceof Object);  // true

但是函數(shù)和對象的關(guān)系卻有一點復(fù)雜,請看下面這個例子:

function Fn() {
  this.name = "嚴新晨";
  this.year = 1990;
}
var fn_1 = new Fn();

由上面這個例子可以得出,對象是可以通過函數(shù)創(chuàng)建的。

但其實,對象都是通過函數(shù)創(chuàng)建的

var obj = { a: 10, b: 20 };
var arr = [5, "x", true];

上面這種方式,其實是一個語法糖,而這段代碼的本質(zhì)是:

var obj = new Object();
obj.a = 10;
obj.b = 20;

var arr = new Array();
arr[0] = 5;
arr[1] = "x";
arr[2] = true;

而其中的ObjectArray都是函數(shù):

console.log(typeof (Object));  // function
console.log(typeof (Array));  // function

由此可以得出,對象都是通過函數(shù)創(chuàng)建的

3、prototype原型

每個函數(shù)都有一個默認屬性 - prototype。

這個prototype的屬性值是一個對象,這個對象有一個默認屬性 - constructor,這個屬性指向這個函數(shù)本身。

而原型作為一個對象,除了constructor之外,當然可以有其他屬性,以函數(shù)Object為例,在瀏覽器調(diào)試窗口輸入Object.prototype會得到以下返回值:

Object
  ...
  constructor
  hasOwnProperty
  isPrototypeOfs
  toLocalString
  toString
  valueOf
  ...

同時,我們還可以為這個原型增添自定義方法或?qū)傩?/p>

function Fn(){}
Fn.prototype.name = "Steven"
Fn.prototype.getYear = function(){
  return 1988;
}

var fn = new Fn();
console.log(fn.name);
console.log(fn.getYear());

在上例中,Fn是一個函數(shù),fn對象是從Fn函數(shù)中new出來的,這樣fn對象就可以調(diào)用Fn.prototype中的屬性。

每個對象都有一個隱藏的屬性 - __proto__,這個屬性引用了創(chuàng)建這個對象的函數(shù)的prototype。即:fn.__proto__ === Fn.prototype

這里的__proto__稱為“隱式原型”。

4、隱式原型

每個函數(shù)function都有一個prototype,即原型。

每個對象都有一個__proto__,可稱為隱式原型。

var obj = {}
console.log(obj.__proto__ === Object.prototype) // true

每個對象都有一個__proto__屬性,指向創(chuàng)建該對象的函數(shù)的prototype

function Foo(){}
Foo.__proto__ === Function.prototype // true
Object.__proto__ === Function.prototype // true 
Function.__proto__ === Function.prototype // true
Function.prototype.__proto__ === Object.prototype // true

注意,Object.prototype.__proto__是一個特例,它指向的是null

5、instanceof

由于typeof在判斷引用類型時,返回值只有objectfunction,這時我們可以用到instanceof。

function Foo(){}
var f = new Foo()

console.log(f instanceof Foo) // true
console.log(f instanceof Object) // true

用法:A instanceof B,變量A是一個待判斷的對象,變量B通常是一個函數(shù)。

判斷規(guī)則:沿著A.__proto__B.prototype查找,如果能找到同一個引用,即同一個對象,則返回true

由以上判定規(guī)則,我們可以解釋許多奇怪的判定結(jié)果,例如:

Object instanceof Function // true
Function instanceof Object // true
Function instanceof Function // true

instanceof表示的是一種繼承關(guān)系 - 原型鏈

6、繼承

JavaScript中的繼承通過原型鏈來體現(xiàn)。

function Foo(){}
var f = new Foo()

f.a = 10

Foo.prototype.a = 100
Foo.prototype.b = 200

console.log(f.a) // 10
console.log(f.b) // 200

上例中,f是Foo函數(shù)new出來的對象,f.a是對象f的基本屬性,因為f.__proto__ === Foo.prototype,所以f.b是從Foo.prototype中繼承而來的。

在JavaScript中,訪問一個對象的屬性時,先在基本屬性中查找,如果沒有,再沿著__proto__這條鏈向上找,這就是原型鏈

通過hasOwnProperty,我們可以判斷出一個屬性到底是基本屬性,還是從原型中繼承而來的。

function Foo(){}
var f_1 = new Foo()

f.a = 10

Foo.prototype.a = 100
Foo.prototype.b = 200

var item

for(item in f){
  console.log(item) // a b
}

for(item in f){
  if(f.hasOwnProperty(item){
    console.log(item) // a
  })
}

hasOwnProperty方法是從Object.prototype中繼承而來的

每個函數(shù)都有apply、call方法,都有length、arguments等屬性,這些都是從Function.prototype中繼承而來的

由于Function.prototype.__proto__指向Object.prototype,所以函數(shù)也會有hasOwnProperty方法

7、原型的靈活性

首先,對象屬性可以隨時改動

其次,如果繼承的方法不合適,可以隨時修改

var obj = { a: 10, b: 20 }
console.log(obj.toString()) // [object Object]

var arr = [1, 2, true]
console.log(arr.toString()) // 1, 2, true

從上例中可以看出,ObjectArraytoString()方法是不一樣的,肯定是Array.prototype.toString()作了修改。

同理,我們也可以自己定義一個函數(shù)并修改toString()方法。

function Foo(){}
var f = new Foo()

Foo.prototype.toString = function(){
  return "嚴新晨"
}

console.log(f.toString) // 嚴新晨

最后,如果缺少需要的方法,也可以自己創(chuàng)建

如果要添加內(nèi)置方法的原型屬性,最好做一步判斷,如果該屬性不存在,則添加。如果本來就存在,就沒必要再添加了。

8、簡述 - 執(zhí)行上下文 - 上

執(zhí)行上下文,也叫執(zhí)行上下文環(huán)境

console.log(a) // 報錯,a is not undefined
console.log(a) // undefined
var a;
console.log(a) // undefined
var a  = 10;
console.log(this) // Window {...}
console.log(f_1) // function f_1({})
function f_1(){} // 函數(shù)聲明

console.log(f_2) // undefined
var f_2 = function(){} // 函數(shù)表達式

在js代碼執(zhí)行前,瀏覽器會先進行一些準備工作:

變量、函數(shù)表達式 - 變量聲明,默認賦值為undefined;

this - 賦值;

函數(shù)聲明 - 賦值;

這三種數(shù)據(jù)的準備工作我們稱之為“執(zhí)行上下文”或者“執(zhí)行上下文環(huán)境”。

JavaScript在執(zhí)行一個代碼段之前,都會進行這些“準備工作”來生成執(zhí)行上下文。這個“代碼段”其實分三種情況 - 全局代碼,函數(shù)體eval代碼。

首先,全局代碼,寫在

以下面代碼為例:

var a = 10, // 1、進入全局上下文環(huán)境
    fn,
    bar = function(x){
      var x = 5
      fn(x+b) // 3、進入fn()函數(shù)上下文環(huán)境
    }

fn = function(y){
  var c = 5
  console.log(y+c)
}

bar() // 2、進入bar()函數(shù)上下文環(huán)境

執(zhí)行代碼前,首次創(chuàng)建全局上下文環(huán)境

a === undefined
fn === undefined
bar === undefined
this === window

代碼執(zhí)行時,全局上下文環(huán)境中的各個變量被賦值

a === 10
fn === function
bar === function
this === window

調(diào)用bar()函數(shù)時,會創(chuàng)建一個新的函數(shù)上下文環(huán)境

b === undefined
x === 5
arguments === [5]
this === window

以上是一段簡短代碼的執(zhí)行上下文環(huán)境的變化過程,一個完整的閉環(huán)。

但實際上,上述情況是一種理想的情況。而有一種很常見的情況,無法做到這樣干凈利落的說銷毀就銷毀,那就是閉包。

12、簡述 - 作用域

JavaScript沒有塊級作用域。所謂的“塊”就是“{}”中的語句,比如:if(){}或者for(){}之類的。

所以,編寫代碼時不要在“塊”里聲明變量。

重點來了:JavaScript除了全局作用域之外,只有函數(shù)可以創(chuàng)建的作用域

所以,在聲明變量時,全局代碼要在代碼前端聲明,函數(shù)中要在函數(shù)體一開始就聲明好。除了這兩個地方,其他地方都不要出現(xiàn)變量聲明。而且建議用“單var”形式。

作用域有上下級的關(guān)系,上下級關(guān)系的確定就看函數(shù)是在哪個作用域下創(chuàng)建的

作用域最大的用處就是隔離變量,不同作用域下同名變量不會有沖突

13、作用域和上下文環(huán)境

在上文中已經(jīng)說過,除了全局作用域之外,每個函數(shù)都會創(chuàng)建自己的作用域。作用域在函數(shù)定義時就已經(jīng)確定了,而不是在函數(shù)調(diào)用時確定。

var a = 10,
    b = 20;
    // 全局作用域:a=10,b=20

function fn(x){
  var a = 100,
      c = 300;
      // fn(10):a=100,c=300,x=10

  function bar(x){
    var a = 1000,
        d = 4000;
        // bar(100):a=1000,d=4000,x=100
        // bar(100):a=1000,d=4000,x=200
  }
  bar(100);
  bar(200);
}
fn(10)

作用域只是一個“地盤”,一個抽象的概念,其中沒有變量
要通過作用域?qū)?yīng)的執(zhí)行上下文環(huán)境來獲取變量的值
**同一個作用域下,不同的調(diào)用會產(chǎn)生不同的執(zhí)行上下文環(huán)境,繼而產(chǎn)生不同的變量的值。
所以,作用域中變量的值是在執(zhí)行過程中產(chǎn)生的確定的,而作用域是在函數(shù)創(chuàng)建時就確定了
所以,如果要查找一個作用域下某個變量的值,就需要找到這個作用域?qū)?yīng)的執(zhí)行上下文環(huán)境,再在其中尋找變量的值。

14、從自由變量到作用域鏈

上文中有一種常見情況我們并沒有討論,那就是跨作用域取值的情況。

先說明一個概念 - 自由變量

var a = 10
function fn(){
  var b = 20
  return a + b // 這里的a就是一個自由變量
}

很多人對此解釋為a是從父作用域取值的,這種說法基本正確,但有些時候會產(chǎn)生歧義。

var x = 10;
function fn(){
  console.log(x) // 10
}

function show(f){
  var x = 20;
  (function(){
    f() // 10,而不是20
  })()
}

show(fn);

所以,更準確的說法是,我們要到創(chuàng)建這個函數(shù)的作用域中去取值。

var a = 10;
function fn() {
  var b = 20;
  function bar() {
    console.log(a + b);
    // 創(chuàng)建函數(shù)bar()時,b=20
    // 通過作用域鏈查找到a=10
  }
  return bar;
}

var x = fn(),
    b = 200;

x(); // 30
15、閉包

先回顧下前面章節(jié)講到的兩個重點:

自由變量跨作用域取值時,要去創(chuàng)建函數(shù)的作用域取值,而不是調(diào)用函數(shù)的作用域;

當一個函數(shù)被調(diào)用完成之后,其執(zhí)行上下文環(huán)境將被銷毀,其中的變量也會被同時銷毀。

“閉包”這個詞的概念很不好解釋,但我們只需記住兩種情況即可:函數(shù)作為返回值和函數(shù)作為參數(shù)傳遞。

首先,函數(shù)作為返回值,先看個例子

// 1、全局作用域,max=100,其他變量undefined
function fn() {
  // 2、fn()作用域,max=10(調(diào)用結(jié)束后銷毀),其他變量undefined
  var max = 10;
  return function bar(x) {
    // 3、bar()作用域,max=10,x=15(調(diào)用結(jié)束后銷毀)
    if (x > max) {
      console.log(x);
    }
  }
}
var f1 = fn(), // bar()作為返回值賦值給f1
    max = 100;
f1(15); // 15

然后,函數(shù)作為參數(shù)傳遞,再看個例子

// 全局作用域,max=10
var max = 10,
    fn = function (x) {
      if (x > max) {
        console.log(x) // 15
      }
    };
(function (f) {
  var max = 100;
  f(15); // max=10而不是100
})(fn);

先回顧下前面章節(jié)講到的兩個重點:

自由變量跨作用域取值時,要去創(chuàng)建函數(shù)的作用域取值,而不是調(diào)用函數(shù)的作用域;

當一個函數(shù)被調(diào)用完成之后,其執(zhí)行上下文環(huán)境將被銷毀,其中的變量也會被同時銷毀。

16、完結(jié) - 這章沒干貨我也就不寫了o(╯□╰)o 17、補充 - this - 我直接寫在10、this那一篇里了就不贅述了 18、補充 - 上下文環(huán)境和作用域的關(guān)系

本篇主要是解釋一下上下文環(huán)境和作用域并不是一回事

上下文環(huán)境 - 可以理解為一個看不見摸不著的對象(有若干個屬性),在調(diào)用函數(shù)時創(chuàng)建,用來保存調(diào)用函數(shù)時的各個變量。

作用域 - 除了全局作用域,只有創(chuàng)建函數(shù)才會創(chuàng)建作用域,無論你是否調(diào)用函數(shù),函數(shù)只要創(chuàng)建了就有一個獨立的作用域。

兩者 - 一個作用域可能包含若干個上下文環(huán)境,也可能從來沒有過上下文環(huán)境(函數(shù)從未被調(diào)用),還可能函數(shù)調(diào)用完畢后上下文環(huán)境被銷毀了等多種情況。

以下面代碼為例:

// 全局作用域中x=100
var x = 100;
function fn(x) {
  // fn(x)作用域
  // 調(diào)用f1()時的上下文環(huán)境中,x=5
  // 調(diào)用f2()時的上下文環(huán)境中,x=10
  return function () {
    // 匿名function作用域
    console.log(x);
  }
}
var f1 = fn(5),
    f2 = fn(10);
f1(); // 5
f2(); // 10

所謂上下文環(huán)境就是調(diào)用函數(shù)時創(chuàng)建的一個臨時作用域,根據(jù)調(diào)用情況不同里面的變量會發(fā)生變化;而作用域是隨著函數(shù)的創(chuàng)建而創(chuàng)建,里面的變量可能會在調(diào)用時被上下文環(huán)境中相同變量覆蓋。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/81366.html

相關(guān)文章

  • JavaScript深入淺出

    摘要:理解的函數(shù)基礎(chǔ)要搞好深入淺出原型使用原型模型,雖然這經(jīng)常被當作缺點提及,但是只要善于運用,其實基于原型的繼承模型比傳統(tǒng)的類繼承還要強大。中文指南基本操作指南二繼續(xù)熟悉的幾對方法,包括,,。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。 怎樣使用 this 因為本人屬于偽前端,因此文中只看懂了 8 成左右,希望能夠給大家?guī)韼椭?...(據(jù)說是阿里的前端妹子寫的) this 的值到底...

    blair 評論0 收藏0
  • 深入理解javascript原型閉包

    摘要:深入理解原型和閉包王福朋博客園深入理解原型和閉包一切都是對象原文鏈接本文要點一切引用類型都是對象,對象是屬性的集合。每個對象都有一個,可稱為隱式原型。另外注意,構(gòu)造函數(shù)的函數(shù)名第一個字母大寫規(guī)則約定。 深入理解javascript原型和閉包 王福朋 - 博客園 —— 《 深入理解javascript原型和閉包》 1. 一切都是對象 原文鏈接:http://www.cnblogs.com...

    jemygraw 評論0 收藏0
  • 深入理解JavaScript,這一篇就夠了

    摘要:也就是說,所有的函數(shù)和構(gòu)造函數(shù)都是由生成,包括本身。如果只考慮構(gòu)造函數(shù)和及其關(guān)聯(lián)的原型對象,在不解決懸念的情況下,圖形是這樣的可以看到,每一個構(gòu)造函數(shù)和它關(guān)聯(lián)的原型對象構(gòu)成一個環(huán),而且每一個構(gòu)造函數(shù)的屬性無所指。 前言  JavaScript 是我接觸到的第二門編程語言,第一門是 C 語言。然后才是 C++、Java 還有其它一些什么。所以我對 JavaScript 是非常有感情的,畢...

    villainhr 評論0 收藏0
  • 深入理解javascript原型閉包

    摘要:情況構(gòu)造函數(shù)所謂構(gòu)造函數(shù)就是用來對象的函數(shù)。另外注意,構(gòu)造函數(shù)的函數(shù)名第一個字母大寫規(guī)則約定。閉包但是你只需要知道應(yīng)用的兩種情況即可函數(shù)作為返回值,函數(shù)作為參數(shù)傳遞。如上代碼,函數(shù)作為返回值,賦值給變量。這就是需要理解閉包的核心內(nèi)容。 原文鏈接http://www.cnblogs.com/wangfupeng1988/p/3977924.html 對象是屬性的集合。 function ...

    _ang 評論0 收藏0
  • JS筆記

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。異步編程入門的全稱是前端經(jīng)典面試題從輸入到頁面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識之 HTTP 協(xié)議 詳細介紹 HTT...

    rottengeek 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<