如今的前端框架層出不窮,3個月就要從新入門一次前端的現狀,讓咱們來不及學好基礎就開始上手框架。經常就由於這樣,咱們會很快到達基礎
基礎技術瓶頸
,基礎是全部技術的核心,在跳槽季從新溫故了一遍 javascript 基礎,有收穫,整理出來分享給你們。
javascript
中全部的變量均可以當作對象使用,除了undefined
和 null
,咱們測試下javascript
false.toString() // "false" [1,2,3].toString() //"1,2,3" 1..toString() //"1" ({a:'33'}).toString() //"[object Object]"
undefined.toString() //Uncaught TypeError null.toString() //Uncaught TypeError
數值和對象雖然能調用 toString
方法,可是在寫法上須要注意下html
number
調用時不能直接數值後面直接調用toString
方法,由於 js
會將點運算符解析爲數值的小數點前端
1.toString() //Uncaught SyntaxError 1..toString() //"1"
對象直接調用toString
方法時,須要用小括號包裹起來,否則js
會將對象的花括號識別成塊,從而報錯java
{a:'33'}.toString() // Uncaught SyntaxError ({a:'33'}).toString() // "[object Object]"
刪除對象的屬性惟一的方法是使用delete
操做符,設置元素屬性爲undefined
或則null
並不能真正刪除,只是移除了屬性和值的關聯
var test = { name:'bbt', age:'18', love:'dog' } test.name = undefined test.age = null delete test.love for (var i in test){ console.log(i+':'+test[i]) }
運行結果es6
name:undefined age:null undefined
只有 love
被正則刪除,name
和 age
仍是能被遍歷到面試
在javascript
中,經過關鍵字new
調用的函數就被認爲是構造函數,咱們能夠經過構造函數建立對象實例
可是在使用過程當中你必定發現了,每實例化一個對象,都會在實例對象上創造構造函數的方法和屬性。假若建立的實例比較多,重複建立同一個方法去開闢內存空間就會顯得十分浪費,咱們能夠經過把被常常複用的方法放在原型鏈上。編程
javascript
和一些咱們所瞭解的面向對象編程的語言不太同樣,在es6
語法之前,咱們是經過原型鏈來實現方法和屬性的繼承
function Child(){ this.name = 'bbt' } Child.prototype = { title:'baba', method: function() {} }; function Grandson(){} //設置 Grandson 的 prototype 爲 Child 的實例 Grandson.prototype = new Child() //爲 Grandson 的原型添加添加屬性 age Grandson.prototype.age = 40 // 修正 Grandson.prototype.constructor 爲 Grandson 自己 Grandson.prototype.constructor = Grandson; var xiaomin = new Grandson() //原型鏈以下 xiaomin // Grandson的實例 Grandson.prototype // Child的實例 Grandson.prototype //{title:'baba',...} Object.prototype {toString: ... /* etc. */};
對象的屬性查找,javascript
會在原型鏈上向上查找屬性,直到查到 原型鏈頂部,因此,屬性在原型鏈的越上端,查找的時間會越長,查找性能和複用屬性方面須要開發者本身衡量下。數組
hasOwnProperty
方法可以判斷一個對象是否包含自定義屬性,而不是在原型鏈上的屬性前端框架
var test = {hello:'123'} Object.prototype.name = 'bbt' test.name //'bbt' test.hasOwnProperty('hello') //true test.hasOwnProperty('name') //false
for in
循環能夠遍歷對象原型鏈上的全部屬性,如此咱們將 hasOwnProperty
結合循環for in
可以獲取到對象自定義屬性微信
var test = {hello:'222'} Object.prototype.name = 'bbt' for(var i in test){ console.log(i) // 輸出兩個屬性,hello ,name } for(var i in test){ if(test.hasOwnProperty(i)){ console.log(i)//只輸出 hello } }
除了上面的方法,getOwnPropertyNames
和 Object.keys
方法,可以返回對象自身的全部屬性名,也是接受一個對象做爲參數,返回一個數組,包含了該對象自身的全部屬性名。
var test = {hello:'222'} Object.prototype.name = 'bbt' Object.keys(test) //["hello"] Object.getOwnPropertyNames(test) //["hello"]
那 getOwnPropertyNames
和 Object.keys
的用法有什麼區別呢
Object.keys
方法只返回可枚舉的屬性,Object.getOwnPropertyNames
方法還返回不可枚舉的屬性名。
var a = ['Hello', 'World']; Object.keys(a) // ["0", "1"] Object.getOwnPropertyNames(a) // ["0", "1", "length"] // length 是不可枚舉屬性
咱們一般會使用函數聲明或函數賦值表達式來定義一個函數,函數聲明和變量聲明同樣都存在提高的狀況,函數能夠在聲明前調用,可是不能夠在賦值前調用
函數聲明
foo(); // 正常運行,由於foo在代碼運行前已經被建立 function foo() {}
函數表達式
foo; // 'undefined' foo(); // 出錯:TypeError var foo = function() {};
變量提高是在代碼解析的時候進行的,foo() 方法調用的時候,已經在解析階段將 foo 定義過了。賦值語句只在代碼運行時才進行,因此在賦值前調用會報錯
一種比較少用的函數賦值操做,將命名函數賦值給一個變量,此時的函數名只對函數內部可見
var test = function foo(){ console.log(foo) //正常輸出 } console.log(foo) //Uncaught ReferenceError
在javascript
中 ,this
是一個比較難理解的點,不一樣的調用環境會致使this
的不一樣指向,可是惟一不變的是this
老是指向一個對象
簡單的說,this
就是屬性和方法當前所在的對象(函數執行坐在的做用域),平時使用的 this
的狀況能夠大體分爲5種
調用方式 | 指向 |
---|---|
1. 全局範圍調用 | 指向 window 全局對象 |
2. 函數調用 | 指向 window 全局變量 |
3. 對象的方法調用 | 指向方法調用的對象 |
4. 構造函數調用 | 指向構造函數建立的實例 |
5. 經過,call ,apply ,bind 顯示的指定 this指向 | 和傳參有關 |
Function.call
語法:function.call(thisArg, arg1, arg2, …),thisArg
表示但願函數被調用的做用域,arg1, arg2, …
表示但願被傳入函數額參數 , 若是參數爲空、null
和undefined
,則默認傳入全局對象。
代碼示例
var name = 'xiaomin' var test = {name : 'bbt'} function hello( _name ){ _name ?console.log(this.name,_name): console.log(this.name) } hello() //xiaomin hello.call(test) //bbt hello.call(test,'xiaohong') //bbt xiaohong hello.call() //xiaomin hello.call(null) //xiaomin hello.call(undefined) //xiaomin
Function.apply
語法和
call
方法相似,不一樣的是,傳入調用函數的參數變成以數組的形式傳入,即 func.apply(thisArg, [argsArray])
改造上面的示例就是
hello.apply(test,['xiaomin'])
Function.bind
bind
方法用於將函數體內的this
綁定到某個對象,而後返回一個新函數。
var d = new Date(); d.getTime() var print = d.getTime; //賦值後 getTime 已經不指向 d 實例 print() // Uncaught TypeError
解決方法
var print = d.getTime.bind(d)
容易出錯的地方
容易出錯的地方,函數調用,this
老是指向 window
全局變量,因此在對象的方法裏若是有函數的調用的話(閉包的狀況),this
是會指向 全局對象的,不會指向調用的對象,具體示例以下
var name = 'xiaomin' var test = { name : 'bbt' } test.method = function(){ function hello(){ console.log(this.name) } hello() } // 調用 test.method() // 輸出 xiaomin
若是須要將 this
指向調用的對象,能夠將對象的 this
指向存儲起來,一般咱們使用 that
變量來作這個存儲。改進以後的代碼
var name = 'xiaomin' var test = { name : 'bbt' } test.method = function(){ var that = this function hello(){ console.log(that.name) } hello() } // 調用 test.method() // 輸出 bbt
閉包咱們能夠理解成是在函數內部定義的函數
在 javascript
中,內部做用域能夠訪問到外部做用域的變量,可是外部做用域不能訪問內部做用域,須要訪問的時候,咱們須要經過建立閉包,來操做內部變量
function test(_count){ var count = _count return { inc:function(){ count++ }, get:function(){ return count } } } var a = test(4) a.get()//4 a.inc() a.get()//5
閉包中常會出錯的面試題
for(var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 0); }
不少同窗會以爲,上面的代碼會正常輸出0到9,可是實際是輸出十次10。遇到這個題目,除了閉包的概念要理解清楚,你還須要知道,setTimeout
內的代碼會被異步執行,代碼會先執行全部的同步代碼,即上面的這段代碼會先將 for
循環執行,此時 i
的值爲 10,console.log(i) 一直引用着全局變量的 i 因此會輸出十次 10
改進代碼,咱們在 for
循環裏建立一個閉包,把循環自增的 i
做爲參數傳入
for(var i = 0; i < 10; i++) { (function(e) { setTimeout(function() { console.log(e); }, 1000); })(i); }
javascript
是異步的單線程運行語言,其餘代碼運行的時候可能會阻塞setTimeout
&&setInterval
的運行
console.log(1) setTimeout(function(){ console.log(2) }, 0); console.log(3) 輸出結果: 1,3,2 //setTimeout 被阻塞
處理阻塞的方法是將setTimeout
和 setInterval
放在回調函數裏執行
function test(){ setTimeout(function(){ console.log(2) }, 0); }
setTimeout
和 setInterval
被調用時會返回一個 ID 用來清除定時器
手工清除某個定時器
var id = setTimeout(foo, 1000); clearTimeout(id);
清楚全部的定時器
var lastId = setTimeout(function(){ console.log('11') }, 0); for(var i=0;i<lastId;i++;){ clearTimeout(i); }
獲取最後一個定時器的id,遍歷清除定時器,能夠清除全部的定時器。
數值、字符串、布爾值——在必定條件下,也會自動轉爲對象,也就是原始類型的「包裝對象」。
咱們能夠經過構造函數,將原始類型轉化爲對應的對象即包裝對象,從而是原始類型可以方便的調用某些方法
數值,字符串,布爾值的類型轉換函數分別是 Number,String,Boolean
,在調用的時候在函數前面加上New 就變成了構造函數,可以蔣對應的原始類型轉化爲「包裝對象」
var v1 = new Number(123); var v2 = new String('abc'); var v3 = new Boolean(true); typeof v1 // "object" typeof v2 // "object" typeof v3 // "object" v1 === 123 // false v2 === 'abc' // false v3 === true // false
類型轉換分爲強制類型轉換和自動轉換,javascript
是動態類型語言,在到嗎解析運行時,須要的數據類型和傳入的數據類型不一致的時候,javascript
會進行自動類型轉化。固然,你也能夠經過類型轉換方法進行強制類型裝換。
平常開發中,咱們最經常使用的數據類型自動轉換不過就下面三種狀況
不一樣數據類型之間相互運算
'2'+4 // '24'
對非布爾值進行布爾運算
if('22'){ console.log('hello') }
對非數據類型使用一元運算符
+'12' //12
咱們也經過 Number ,String,Boolean
來進行強制數據類型轉換。強制類型轉化的規則有點複雜,咱們來了解一下。
Number 轉換 引用阮老師的詳細解釋
第一步,調用對象自身的valueOf方法。若是返回原始類型的值,則直接對該值使用Number函數,再也不進行後續步驟。 第二步,若是 valueOf 方法返回的仍是對象,則改成調用對象自身的 toString 方法。若是 toString 方法返回原始類型的值,則對該值使用 Number 函數,再也不進行後續步驟。 第三步,若是 toString 方法返回的是對象,就報錯。
String
轉換方法一樣也是經過調用原對象的 toString
方法和 valueOf
方法,可是不一樣的是 String
函數會先調用 toString
方法進行轉換
Boolean
的轉換規則會相對簡單一些,除了幾個特殊的值,都會被轉化爲 true
undefined null +0或-0 NaN ''(空字符串)
可是要注意
Boolean('false') //true
typeof
操做符返回數據類型,可是因爲javascript
設計的歷史緣由,typeof
現已經不能知足咱們如今對於類型判斷的要求了
Value | Class | Type |
---|---|---|
"foo" | String | string |
new String("foo") | String | object |
1.2 | Number | number |
new Number(1.2) | Number | object |
true | Boolean | boolean |
new Boolean(true) | Boolean | object |
new Date() | Date | object |
new Error() | Error | object |
[1,2,3] | Array | object |
new Array(1, 2, 3) | Array | object |
new Function("") | Function | functio |
/abc/g | RegExp | object (function in Nitro/V8) |
new RegExp("meow") | RegExp | object (function in Nitro/V8) |
{} | Object | object |
new Object() | Object | object |
null | null | object |
咱們能夠看到,typeof
不能區分對象的數組和日期,還會把 null
判斷成對象,那咱們通常是何時用 typeof
呢。咱們能夠用來判斷一個已經定義的變量是否被賦值。
var a if(typeof a == 'undefined'){ console.log('a 已經被定義') }
instanceof
操做符一般用來判斷,一個對象是否在另外一個對象的原型鏈上,須要注意的是instanceof
的左值是對象,右值是構造函數
// defining constructors function C() {} function D() {} var o = new C(); // true, because: Object.getPrototypeOf(o) === C.prototype o instanceof C; // false, because D.prototype is nowhere in o's prototype chain o instanceof D;
#### Object.prototype.toString
那麼咱們有沒有能夠用來區分變量數據類型的方法呢,有,
Object.prototype.toString
一些原始數據類型也有 toString
方法,可是一般他們的 toString
方法都是改造過的,不能進行 數據類型判斷,因此咱們須要用 Object
原型鏈上的 toString
方法
var a = 1234 a.toString() // '1234' Object.prototype.toString.call(a) // "[object Number]"
不一樣類型返回的結果以下:
1. 數值 [object Number] 2. 字符串 [object String] 3.布爾值 [object Boolean] 4.undefined [object undefined] 5.null [object Null] 6.數組 [object Array] 7.arguments [object Arguments] 8.函數 [object function] 9.Error [object Error] 10.Date [object Date] 11.RegExp [object RegExp] 12.其餘對象 [object object]
那麼咱們就可以經過 Object.prototype.toString
方法,封裝一個能夠判斷變量數據類型的函數了
function type(obj) { return Object.prototype.toString.call(obj).slice(8, -1); } type(function(){}) //"Function"
此次咱們從對象、函數、類型三方面入手瞭解了
javascript
中容易被忽視或則說比較難理解的地方,我會繼續將我在學習中積累的內容分享給你們,若是你們以爲文章有須要改進或則有其餘想要了解的內容的,歡迎私信,評論或則微信我,646321933