你們好,本系列文章是總結我學習ES5的一些筆記以及一些認爲比較重要的知識點,其中在ES部分有以下整理:html
基石:ES5基礎(一) 數據類型&類型轉換面試
基石:ES5基礎(二) 對象&對象特徵屬性算法
基石:ES5基礎(三) 原型&原型鏈&繼承數組
基石:ES5基礎(四) 函數&做用域&閉包bash
我知道,有不少大佬已經hold這些知識點,而且很是高贊。但人家的終歸是人家的.. 沒法親力親爲,何談印象深入。閉包
首先感謝各位閱讀本文章,因爲弟弟剛學不久,雖然對語言的高度認識還不夠,對寫文檔的能力弱一逼,以及總結概括技巧很是拙劣。可是我知道你們都是敞亮人都是哥哥,會鞭策好我讓新萌獲得最快成長,謝謝你們的海涵!!函數
首先先扔幾道高頻面試題來開始咱們本次ES5之旅。post
請描述一下原始值
和引用值
的區別學習
請簡單解釋一下 0.1 + 0.2 != 0.3
ui
簡述 1.abc=123
經歷了哪些過程
4 + [1,2,3]
、'a' + + 'b'
以及[] == ![]
分別返回什麼?
判斷類型
的方法以及不一樣,瞭解過$.type
嗎?
在ES規範
中規定了數據類型分爲兩大陣營,一方是原始值,另一方是引用值。我剛接觸ES的時候就一直有一個疑惑,爲何計算機要設計原始值以及引用值?直接放一個鍋裏不行嗎?後來當我學過堆和棧的概念以後,彷彿理解了一些。
首先回顧一下原始值和引用值分別是什麼。
6.1ECMAScript Language Types
An ECMAScript language type corresponds to values that are directly manipulated by an ECMAScript programmer using the ECMAScript language. The ECMAScript language types are Undefined, Null, Boolean, String, Symbol, Number, and Object. An ECMAScript language value is a value that is characterized by an ECMAScript language type.
manipulate:操縱
characterized by:特色
在ECMA標準中,規定了ES有以下幾種數據類型供給咱們操做
Undefined Null Boolean String Symbol(ES6新增) Number 以及 Object.
經過ECMA標準以及在紅寶書上咱們學習到對於數據類型的講解和分類,分爲原始值和引用值。首先看一下在ECMA標準中是如何定義着7種數據類型。
6.1.1The Undefined Type
The Undefined type has exactly one value, called undefined. Any variable that has not been assigned a value has the value undefined.
assign: 指派
exactly: 確切
確切的說Undefined類型只有一個值,是undefined。表示任何的變量若是沒有被分配值那麼默認是undefined.注意和null的區別
6.1.2The Null Type
The Null type has exactly one value, called null.
Null類型只有一個值,是null.官方文檔沒有給出額外的信息,可是在紅寶書中說過
從邏輯的角度講null是用來表示一個空指針,而且typeof返回object
。一般是用來表示一個對象已經聲明,最好使用null來初始化而不是其餘值。
根據ECMA-262
規定對他們的相等性判斷是返回true,其根本緣由是undefined是派生自null。
可是他們的語義和用途是不一樣的.任何狀況下咱們都不必對變量顯示賦值爲undefined。對於對象變量尚未保存對象時,咱們應該明確的讓此對象等於null.
6.1.3The Boolean Type
The Boolean type represents a logical entity having two values, called true and false.
represents: 表明
logical: 邏輯學
6.1.4The String Type
The String type is generally used to represent textual data in a running ECMAScript program, in which case each element in the String is treated as a UTF-16 code unit value.
treat: 對待
String類型一般用於表示正在運行的程序中文本數據,每一個元素都被視爲UTF-16代碼單元值。
6.1.6The Number Type
The Number type has exactly 18437736874454810627 (that is, 264-253+3) values, representing the double-precision 64-bit format IEEE 754-2008 values as specified in the IEEE Standard for Binary Floating-Point Arithmetic, except that the 9007199254740990 (that is, 253-2) distinct 「Not-a-Number」 values of the IEEE Standard are represented in ECMAScript as a single special NaN value.
specifiey: 指定
distinct: 明顯的
Number類型中能表示那麼多的值,由於採用的是IEEE二進制雙精度浮點數運算標準。這爲0.1 + 0.2 != 0.3埋下了伏筆。在ECMA中,使用一個特殊值NaN表示不在標準範圍內的值。
all NaN values are indistinguishable from each other.
indistinguishable: 難分辨
全部的NaN彼此之間是沒法區分的,也就是說 NaN == NaN爲 false
,可是請注意在ES6中NaN使用Object.is來判斷是相等的。
Note that there is both a positive zero and a negative zero. For brevity, these values are also referred to for expository purposes by the symbols +0 and -0, respectively.
negate: 取消/負的
注意,在ECMA中,對於0是分+0和-0的。咱們知道在ES5中對於-0 ===+0
爲true.可是在ES6使用Object.is判斷爲不相等。
6.1.7The Object Type
An Object is logically a collection of properties. Each property is either a data property, or an accessor property:
對象在邏輯上是屬性的集合,每一個屬性或者是數據屬性,或者是訪問器屬性。本身分一類絲絕不過度,除了經常使用的Object
,Array
、Function
等都屬於特殊的對象;數據屬性和訪問器屬性會在對象部分詳解。
簡單回顧後,引出核心問題之一,簡述一下原始值和引用值的區別?
JavaScript中的原始類型的值被直接存儲在棧中,在變量定義時,棧就爲其分配好了內存空間,因爲棧中的內存空間的大小是固定的,那麼註定了存儲在棧中的變量就是不可變的。 棧內存特色:
多數狀況下,基本類型直接表明了最底層的語言實現。
全部基本類型的值都是不可改變的。但須要注意的是,基本類型自己和一個賦值爲基本類型的變量的區別。變量會被賦予一個新值,而原值不能像數組、對象以及函數那樣被改變。
var str = "SU";
str.slice(1);
console.log(str) // SU;
str += "1";
console.log(str) // SU1;
複製代碼
當咱們調用方法後,是在原字符串的基礎上進行操做產生一個新的字符串,而並不是直接修改str,着印證了字符串的不變性
。 但你會發現第二次str被修改了,看似是str被改變了實際上是發生了下面的代碼
var str = str + 1;
//等同於str += 1;
複製代碼
是由於JavaScript中的原始值存放在棧
中,在變量定義的同時就分配好了內存空間。因此 str += 1其實是在內存中開闢了一個新空間
,並不是是改變字符串的值,並不違原始類型的不可變性。
引用類型的值實際存儲在堆內存中,它在棧中只存儲了一個固定長度的地址,這個地址指向堆內存
中的值.
var spy = {
age : 23,
}
console.log(spy) //age : 23,
spy[daughter] = "qianqian";
console.log(spy) //age : 23,daughter : qianqian
複製代碼
印證了引用類型的可變性。
堆內存特色:
原始值複製後的變量指向的內存空間徹底不一樣,變量參與任何操做都互不影響。
var foo = 123;
var bar = foo; //bar : 123
bar ++; // 124
console.log(foo) // 123
複製代碼
引用值複製的是棧中存儲的地址,會指向堆內存中同一個對象。改變其中一個任意一個變量的數據就會致使相同地址的變量發生改變。
var spy = {
age : 23,
}
spy[daughter] = "qianqian";
var lcy = spy;
lcy[sex] = "female" ;
console.log(lcy); //age:23,sex:female,daughter:qianqian
console.log(spy); //age:23,sex:female,daughter:qianqian
複製代碼
將spy這個對象複製給lcy對象後,操做lcy對象進行設置屬性和賦值,會發現spy對象也被一樣操做。
所以一般使用淺拷貝和深拷貝來實現對引用值進行復制。
對於原始類型,比較時會直接比較它們的值,若是值相等,即返回true。
var a == 1;
var b == 1;
console.log(a == b) // true;
b = 3;
console.log(a == b) // false;
複製代碼
對於引用類型,比較時會比較它們的引用地址,雖然兩個變量在堆中存儲的對象具備的屬性值可能相等,可是它們被存儲在了不一樣的存儲空間,所以比較值爲false。
var arr1 = [1];
var arr2 = [1];
console.log(arr1 == arr2) //false;
複製代碼
函數參數傳遞的並非變量的引用,而是變量拷貝的副本,當變量是原始類型時,這個副本就是值自己;當變量是引用類型時,這個副本是指向堆內存的地址。因此,請默唸三遍:ECMAScript中全部函數的參數都是按值傳遞的
function add(num){
num++;
return num;
}
var a = 1;
console.log(add(1),a); // 2 1;
複製代碼
當原始值1做爲函數參數傳遞給函數時,可以證實是傳遞的值自己。就和原始值賦值是同樣的性質(本質是arugments作緩衝層)
function setName(obj){
obj[name] = "spy";
return obj;
}
function createNew(obj){
obj[name] = "spy";
obj = new Object(); //notice
return obj;
}
var obj = new Object();
setName(obj);
console.log(obj); // spy;
var newObj = createNew(obj);
console.log(obj);// spy;
console.log(newObj); // {}
複製代碼
解釋一下notice標記的代碼,此時的obj是隸屬於createNew函數的內部變量,在函數執行結束後會當即銷燬。在這裏只是更新了內部同名屬性obj
的指針,可是不會影響外部的obj。所以hold參數是按值傳遞的。
請默唸三遍:ECMAScript中全部函數的參數都是按值傳遞
在ECMA標準中,還規定了3種特殊引用類型:Boolean
Number
String
統稱爲基本類型包裝類,他和引用類型同樣,也具備與各自的基本類型相應的特殊行爲.
var name = "Su";
var length = name.length();
複製代碼
咱們知道,基本類型值不是對象,於是從邏輯上 講它們不該該有方法(儘管如咱們所願,它們確實有方法.其實,爲了讓咱們實現這種直觀的操做,後臺已經自動完成了一系列的處理。
所以能夠理解成以下
var name = new String("Su");
var length = name.length();
name = null;
複製代碼
這樣就能夠將基本的字符串和對象同樣看待。同時分別適用於Boolean和Number。
注意undefined和null沒有基本包裝類
,也就意味着他們沒有方法,設置不了屬性。
undefined.name = 'Su'; //Uncaught TypeError: Cannot set property 'name' of undefined
undefined.length(); //Uncaught TypeError: Cannot read property 'length' of undefined
複製代碼
由於undefined是派生自null,所以null也一樣沒有方法和沒法設置屬性。所以能夠經過包裝類的角度
解釋爲何undefined和null的調用方法和設置屬性會報錯。
引用類型與基本包裝類型的主要區別就是對象的生存期
自動建立的基本包裝類型的對象,則只存在於一 行代碼的執行瞬間,而後當即被銷燬。這意味着咱們不能在運行時爲基本類型值添加屬性和方法。
能夠通俗的理解爲給基本類型設置屬性後,再次訪問這個屬性會返回undefined(非嚴格模式)。
var spy = "Su";
spy.sex = "male";
console.log(spy.sex); // undefined;
複製代碼
在這裏只是簡單的介紹了一下基本包裝類,在後面的學習過程當中,會詳細的剖析。
由於ECMASciprt是弱語言,因此常常會發生類型轉換,例如剛纔闡述的基本包裝類也屬於一種隱式類型轉換。
類型轉換分爲兩種,隱式轉換即程序自動進行的類型轉換,強制轉換即咱們手動進行的類型轉換。
在這裏咱們並不討論API層面的強制轉換,Focus在隱式類型轉換。
在if
語句或者邏輯語句時,若是隻有單個變量,會先將變量轉換爲Boolean
值,只有下面幾種狀況會轉換成false
,其他被轉換成true
:
null undefined ' ' NaN 0 false
首先咱們要知道,在 JS 中類型轉換隻有三種狀況,分別是:
在條件判斷時,除了 undefined
, null
, false
, NaN
, ''
, 0
, -0
,其餘全部值都轉爲 true
,包括全部對象。
對象發生類型轉換時,會調用內部的[[ToPrimitive]]
規則,這個算法邏輯一般能夠總結爲:
x.valueOf()
,若是轉換爲原始類型,就返回轉換的值x.toString()
,若是轉換爲原始類型,就返回轉換的值'[oject Array]' == [ ] // true
'1,2,3' == [1, 2, 3] // true
複製代碼
同時也能夠利用 Symbol.toPrimitive
,重寫[[ToPrimitive]]
規則。該方法在轉原始類型時調用優先級最高。
var test = {
valueof () {
return 1;
},
toString() {
return 2;
},
[Symbol.toPrimitive]() {
return 3;
}
}
var a = 1;
a + test; // 4
複製代碼
同時要注意類型轉換和優先級的組合
[] == ![]
左側根據[[ToPrimitive]]
轉換成0,!
的優先級大於==
因此會將[]轉化成false後進行比較。
+
運算符不一樣於其餘運算符,總結其特色爲:
1 + "abc" // "1abc" notice 1
true + true // 2 notice 2
4 + [1,2,3] // "41,2,3" notice2 和對象轉原始值
複製代碼
在作+
運算時,要注意優先級,例如
"a" + + "b"
結果爲aNaN
能夠理解爲a +(+"b")
一般狀況下+ "1"
是用來強制轉換。
*、-、/
運算符就相對簡單一些,要其中一方是數字,那麼另外一方就會被轉爲數字
4 * '3' // 12
4 * [] // 0
4 * [1, 2] // NaN
複製代碼
==
操做符對比雙方類型不同的話就會發生類型轉換。
判斷流程爲:
boolean
,是的話就會把 boolean
轉爲 number
再進行判斷object
且另外一方爲string
、number
或者 symbol
,是的話就會把 object
轉爲原始類型再進行判斷。a == 1 && a == 2 && a == 3
如何分析呢?
這是在ConardLi大佬博客中看到的一道題,關於變量和類型
大佬講的更細緻,我會在最後將文章連接放出來。
可以判斷原始類型,除了null(二進制000問題)
typeof
對於原始類型來講,除了 null
均可以顯示正確的類型
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
複製代碼
沒法判斷引用值,除了Function類型
typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'
複製代碼
內部機制是經過原型鏈進行判斷,能夠判斷引用值類型,一般狀況下是沒法對原始值進行判斷。
const Person = function() {}
const p1 = new Person()
p1 instanceof Person // true
var str = 'hello world'
str instanceof String // false
var str1 = new String('hello world')
str1 instanceof String // true
複製代碼
若是此方法在自定義對象中未被覆蓋,toString纔會達到預想的效果進行類型判斷。事實上,大部分引用類型好比Array、Date、RegExp等都重寫了toString方法,所以咱們一般使Object原型未被覆蓋的toString()方法。
若是本篇文章您以爲有什麼地方有問題,必定要給我指正。若是您以爲還不錯,而且期待後續文章,點贊關注走一波~
梳理一下欠下待整理的文章:
ConardLi大佬:【JS 進階】你真的掌握變量和類型了嗎