一、es6中的箭頭函數和普通函數有什么區別?
1、普通函數中的?this總是指向調用它的那個對象,
箭頭函數沒有自己的this,他的this永遠指向其定義環境,任何方法都改變不了其指向,如call()、bind()、apply()。(正是因為它沒有this,所以也就不能用作構造函數,也沒有原型對象)
箭頭函數不能當作構造函數,也就是說,不能使用new命令,否則會報錯。
箭頭函數沒有原型屬性。
箭頭函數不可以使用yield命令,因此箭頭函數不能用作Generator函數。
箭頭函數不能使用arguments對象,該對象在函數體內不存在。如果要用,可以用rest參數代替。
變量提升:由于js的內存機制,function的級別最高,而用箭頭函數定義函數的時候,需要var(let、const)關鍵字,而var所定義的變量不能得到變量提升。故箭頭函數一定要定義于調用之前。
拓展:this的指向問題?
? ? 1、普通函數中,this指向其函數的直接調用者;
? ? 2、箭頭函數中,this指向其定義環境,任何方法都改變不了其指向,如call( )、bind()等;
? ? 3、構造函數中,如果不使用new,則this指向window,
?? ?? ? 如果使用new創建了一個實例,則this指向該實例。
? 4、window內置函數中,如setInterval,setTimeout等,其內部的this指向Window。
? ? 5、匿名函數的this指向Window。
? ? 6、apply()、call()、bind()可以改變this的指向
二、談談你對原型鏈的認識?
https://blog.csdn.net/xiaotao_css/article/details/72782416:通俗易懂的介紹(僅供18歲以上成年閱讀)
看下面的東西之前,建議先看上面鏈接的這篇文章
? ? 對象: 1 、 函數對象: 由 function 創造出來的函數,比如 function a(){ } ; 系統內置的函數對象: Function , Object , Array , String , Number
?? ??? ??? ?? ? 2、 普通對象: 除開函數對象之外的對象,都是普通對象
?? ??? ??? ?? ?每個普通對象的__proto__屬性,都指向Object().prototype?,
?? ??? ??? ?? ? var obj = { }? 就等于? var obj = new Object ( ) ;? 即普通對象是? 構造函數( Object ) 的一個實例
?? ??? ??? ?? ? 所以? obj. __proto__? ? ===? Object.prototype? ( 但是老高說的,Object的原型還是object對象, )
?? ??? ??? ??? ??? ??? ?? obj. constructor? ===? Object?
?? ?凡是通過 new Function() 創建的對象都是函數對象,其他的都是普通對象。
? ? 注:所有對象都有 __proto__ 屬性只有函數對象才有 prototype 屬性 ?。。。。。。。。。?!
? ? 原型對象: prototype 屬性也叫原型對象,主要是為了實現繼承;
?? ??指針 __proto__ :? js中,萬物皆對象!所有的對象 obj 都具有 proto 屬性(null 和 undefined除外 ),而且指向創造obj 對象的函數對象(生成實例的構造函數)的prototype屬性 。 如以下例子:
Person 構造函數的原型對象 是 Mother ()。相當于原型是媽媽,Person現在是兒子。
在 p1 和 p2 實例中,__proto__屬性,指向的是 創造他們的構造函數Person 對象的 prototype 屬性,所對應的對象。
也就是 Mother().
一個構造函數對象的原型,就相當于 他媽,這個構造函數對象的實例,就相當于? 他媽不同的孩子。
而 每個 實例中的 __proto__屬性,就指向 他們共同的 媽 !也就是 構造函數對象的 prototype屬性。
當我們輸入?p1.name?的時候,原型鏈的搜索機制是先在實例中搜索相應的值,找不到就通過它的__proto__指針,在原型中找,還找不到就再往上一級原型中搜索……一直到了原型鏈的終點( 就是js自帶的Object,它的原型比較特殊,為null ),就是到null還沒找到的話,就返回一個?undefined。
? ? 構造器constructor : 每一個對象中的constructor 屬性返回創建此對象的函數對象的引用;例如:
? ? ?functon? Dog ( name, color ) {?
?? ?? ? this. name = name;
?? ?? ? this. color = color;
?? ?}
? ? var dog1 = new Dog( "小白" , "白色" );
? ? dog1.constructor == Dog 構造函數本身
在默認情況下,所有的原型對象都會自動獲得一個 constructor(構造函數)屬性,這個屬性(是一個指針)指向 prototype 屬性所在的函數(Person)
上面這句話有點拗口,我們「翻譯」一下:A 有一個默認的 constructor 屬性,這個屬性是一個指針,指向 Person。即:
Person.prototype.constructor == Person
實例的構造函數屬性(constructor)指向構造函數 :person1.constructor == Person
person1 為什么有 constructor 屬性?那是因為 person1 是 Person 的實例。
那 Person.prototype 為什么有 constructor 屬性??同理, Person.prototype (你把它想象成 A) 也是Person 的實例。
也就是在 Person 創建的時候,創建了一個它的實例對象并賦值給它的 prototype,基本過程如下:
var A = new Person();
Person.prototype = A;
結論:原型對象(Person.prototype)是 構造函數(Person)的一個實例。
以下代碼的圖示?
?? ??? ??? ?function Foo ( ) { } ;
var f1 = new Foo;
以下是 深入解答原型是怎么回事 的一篇文章?
1、http://www.lxweimin.com/p/dee9f8b14771
2、http://www.lxweimin.com/p/652991a67186
3、http://www.lxweimin.com/p/a4e1e7b6f4f8? ??
原型鏈的概念
如果問原型鏈是什么,直接把下面的這張圖畫出來就行了。
https://www.cnblogs.com/shuiyi/p/5305435.html
騰訊大學原生JS 視頻 :https://ke.qq.com/course/231577
回答出以下問題,就知道 __proto__和prototype 的什么了
function Person(){? ?} ;
var person1 = new Person( );
person1.__proto__?是什么?
Person.__proto__?是什么?
Person.prototype.__proto__?是什么?
Object.__proto__?是什么?
Object.prototype__proto__?是什么?
答案:
第一題:
因為 person1.__proto__ === person1 的構造函數.prototype
因為 person1的構造函數 === Person
所以person1.__proto__ === Person.prototype
第二題:
因為 Person.__proto__ === Person的構造函數.prototype
因為 Person的構造函數 === Function
所以Person.__proto__ === Function.prototype
第三題:
Person.prototype 是一個普通對象,我們無需關注它有哪些屬性,只要記住它是一個普通對象。
因為一個普通對象的構造函數 === Object
所以Person.prototype.__proto__ === Object.prototype
第四題,參照第二題,因為 Person 和 Object 一樣都是構造函數
?? ??? ??? ?? ?不要把 Object 想的太復雜,它其實也不過是 Function 構造出來的一個 方法 (一個普通的構造函數)
?? ??? ??? ?? ? 所以 Object.__proto__ === Function.prototype
第五題:
Object.prototype 對象也有proto屬性,但它比較特殊,為 null 。因為 null 處于原型鏈的頂端,這個只能記住。
Object.prototype.__proto__ === null
記?。簆rototype是一個普通對象,所有普通對象的__protp__都指向 Object.prototype
三、js跨域問題怎么解決?
? ?1、?JSONP跨域請求
? ? 要理解跨域,先要了解一下”同源策略“。所謂同源是指,協議、域名、端口都相同。所謂”同源策略“,簡單的說,就是基于安全考慮,當前域不能訪問其他域的東西。
http?和?https :協議不同
www.a.com?: 8080?和www.a.com?: 1000? ?:? ?端口不同
在同源策略下,在某個服務器下的頁面是無法獲取到該服務器以外的數據的。
例如我們在自己的網站通過 ajax?去?獲取豆瓣上的圖書接口:
https://api.douban.com/v2/book/search?q=javascript&count=1
我們通過以上 ajax?去訪問,發現運行時會報錯:
只要出現這個錯誤,就說明服務器接口不支持跨域
//No 'Access-Control-Allow-Origin' header is present on the requested resource
這是因為不同源,所以無法訪問其他服務器的數據
但是<img>?的 src (?獲取圖片 ) ,? <link>?的 href (獲取css),<script>?的?src (獲取js)這三個屬性都不符合同源策略,它們可以跨域獲取數據。JSONP就是利用<script>?的 src?來實現跨域獲取數據。
跨域原理
? ? JSONP實現跨域請求的原理,簡單的說,就說動態創建 <script>標簽,然后利用<script>的?src?不受同源策略的約束來跨域獲取數據。
? ? JSONP?由兩部分組成:回調函數和數據?;卣{函數?是當響應到來時,應該在頁面中調用的函數。回調函數的名字,一般是在請求中指定的。而數據就是傳入回調函數中的 JSON?數據。
注意:JSONP不是真正的?ajax?
ajax是異步的,jsonp是同步的,所以它不是真正的ajax
動態創建 <script>?標簽,設置其?src ,回調函數在 src?中設置:
var script = document.createElement("script");
script.src = "https://api.douban.com/v2/book/search?q=javascript&count=1";
document.body.insertBefore( script, document.body.firstChild );
在頁面中,返回的 JSON?作為參數傳入?回調函數中,我們通過回調函數來?操作數據
function handleResponse(response){
? ? //對 response?數據進行操作代碼
}
?了解了 JSONP?的基本使用方法,我們在實現上面,通過 ajax?調用豆瓣接口的需求,實現代碼如下:
注意:以上代碼中,要記得成功訪問完數據后,要刪除創建的動態 <script>標簽:
document.body.removeChild(script)
其實在接口數據的形式類似:fn( { name: "張三" , age: "20" } ) ,我們傳遞過去一個和這個函數名字相同的回調函數,參數就是訪問到的數據。
假如接口是:http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice
函數名就是 callbanck =?的值:refreshPrice.
JSONP目前還是比較流行的跨域方式,雖然JSONP使用起來方便,但是也存在一些問題:如果其他域不安全,很可能會在響應中夾帶一些惡意代碼。而且要確定 JSONP請求是否失敗并不容易。
JSONP有個限制,只能用GET請求,并且要求返回JavaScript
更多跨域的方法介紹:
對axios有更進一步的理解,利用cros進行跨域處理?。?!
2、CROS跨域
3、反向代理
四、閉包
? ? 概念: 閉包就是能夠讀取其他函數內部變量的函數。
? ? 由于函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成“定義在一個函數內部的函數”(然后將這個內部的函數 return 返回出來)。
? ? 所以,在本質上,閉包就是將函數內部和函數外部連接起來的一座橋梁。
? ? 閉包的作用:它最大的用處有兩個: 1) 一個是可以讀取函數內部的變量;
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?? ?2) 另一個就是讓這些變量的值始終保存在內存中。
? ? 使用閉包的注意點:1) 由于閉包會使得函數中的變量都保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄漏。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。
?? ??? ??? ?? ???? ??? ??? ??? ?? ? 內存泄漏:程序的運行需要內存。對于持續運行的服務進程,必須及時釋放不再用到的內存,否則占用越來越高,輕則影響系統性能,重則導致進程崩潰。不再用到的內存,沒有及時釋放,就叫做內存泄漏。
http://www.ruanyifeng.com/blog/2017/04/memory-leak.html
?? ??? ??? ??? ??? ??? ??? ?? ? ?? 2) 閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法,把內部變量當作它的私有屬性,這時一定要小心,不要隨便改變父函數內部變量的值。
五、promise的用法和原理?
基礎https://mengera88.github.io/2017/05/15/promise%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/
原理
https://segmentfault.com/a/1190000009478377
?概念:promise 是異步編程的一種解決方案。它可以把異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。
?Promise 為異步操作提供了統一的接口,使得控制異步操作更加容易,它的強大之處在于它的鏈式調用。
基本用法:
newPromise(function(resolve, reject){
//待處理的異步邏輯
//處理結束后,調用resolve或reject方法
})
新建一個promise很簡單,只需要new 一個 Promise 對象即可。所以promise本質上就是一個函數,它接受一個函數作為參數,并且返回promise對象,這就給鏈式調用提供了基礎。
特點:
1、對象的狀態不受外界影響。
Promise 的實例 有以下三種狀態:
? ? 1)pending : 進行中
? ? 2)resolved : 已成功完成
? ? 3)rejected : 已失敗
只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。
2、一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從pending變為resolved;從pending變為rejected。 只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。
基本用法:
ES6規定,Promise 對象是一個構造函數,用來生成Promise實例
Promise構造函數接受一個函數作為參數,該函數的兩個參數分別是resolve和reject。它們是兩個函數,由JavaScript 引擎提供, 不是自己部署。
resolve函數的作用,將Promise對象的狀態從“進行中”變成 “成功”( 即從pending變為resolved ) ,在異步操作成功時調用,并將異步操作的結果,作為參數傳遞出去。
reject函數的作用,在異步操作失敗時調用,并將異步操作報出的錯誤,作為參數傳遞出去。
Promise實例生成以后,可以用then方法分別制定 Resolved狀態和Rejected狀態的回調函數:
then方法可以接受 2 個回調函數作為參數,第二個函數是可選的,不一定要提供。這兩個函數都接受Promise對象傳出的值作為參數。
promise捕獲錯誤? .catch方法:
Promise.prototype.catch方法是Promise.prototype.then(null, rejection)的別名,用于指定發生錯誤時的回調函數。
Promise對象的錯誤具有“ 冒泡 ”性質,會一直向后傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch語句捕獲。
題外話:async 函數是es7 提案出來的語法, async函數是用來取代回調函數的另一種方法。