本文參照
大神文章,也是本身複習準備面試的小筆記總結
1.原型/構造函數/實例/原型鏈
2.做用域/做用域鏈複製代碼
做用域/做用域鏈
在講做用域概念以前,先了解一個概念,執行上下文環境(Execution Context)
-
變量對象:執行上下文的一部分,能夠抽象爲一種數據做用域,存儲該執行上下文中的全部變量和函數聲明(不包含函數表達式)。
活動對象(AO):當變量對象處於active EC的時候,稱其爲活動對象。 說到活動對象,又要講一下js的詞法執行順序: 每一個函數執行的瞬間,就會生成一個對象,即活動對象AO 1. 先分析參數: 函數先接收形參,將這些形參賦值給AO的屬性,初始值都爲undefined 而後接受實參,將實參賦值給屬性; 2.分析變量的聲明: 若是AO裏邊沒有這個屬性,則新增該屬性;若存在,則不做任何處理; 3.分析函數 若是AO屬性裏邊沒有該屬性,則新增屬性;若存在,則被新值覆蓋;
function t(age) {
console.log(age);//1
var age = 99;
console.log(age);//2
function age() {
}
console.log(age);//3
}
t(5);複製代碼
1:function age(){}
2: 99
3: 99
分析:
這時候還涉及到一個變量提高的問題;在同一做用域,變量和函數優先聲明,可是賦值不優先;
按照上邊的過程一步步分析:
1. 在t函數被調用的瞬間會生成一個活動變量AO,分析形參,將age付給AO的一個屬性,而且賦值爲5;
2. 在函數內部分析變量,age變量和函數的聲明都提高到函數的最上邊,也就是1的上邊;
當聲明變量的時候,發現AO的屬性裏有age這個屬性,不作任何操做;
而後聲明函數,聲明函數的時候,發現有該屬性,則把函數賦值給該屬性;
因此在1打印的時候AO.age爲age函數
3.執行到age = 99的時候,又將AO.age從新賦值給99
因此2打印爲99,2-3之間沒有作任何改變值的操做,因此3處也爲99;複製代碼
-
全局執行上下文
-
函數執行上下文
-
eval執行上下文
-
建立全局上下文(global EC)
-
全局執行上下文(caller)逐行自上而下執行,遇到函數時,函數執行上下文被push到執行棧的頂層
-
函數執行上下文被激活,成爲active EC,開始執行函數中的代碼,caller被掛起
-
函數執行完,callee被pop移除出執行棧,控制權交換給全局上下文(caller),繼續執行
var a = 1;
function b() {
console.log(a);
}
function c() {
var a = 3;
b();
}
c();複製代碼
能夠理解爲:父函數被銷燬的狀況下,返回出的子函數的[[scope]]中仍然保留着父級的單變量對象和做用域鏈,所以能夠繼續訪問到父級的變量對象,這樣的函數稱爲閉包。
閉包因爲存在局部變量中一直不能被釋放,因此對性能有負面影響,因此能不用閉包儘可能不用。
function test(){
for(var i = 0;i < 5; i++) {
setTimeout(() => {
console.log(i);
},10)
}
}
test();複製代碼
由於setTimeout 是一個異步操做,當settimeout的回調被執行的時候,i已經爲5了
function test(){
for(var i = 0;i < 5; i++) {
(function(val){
setTimeout(() => {
console.log(i);
},10)
})(i);
}
}
test();複製代碼
function count() {
var count = 0;
var add = function() {
count++;
console.log(count);
}
return add;
}
var add = count();
add();// 1
add();// 2複製代碼
柯里化
簡單來講,就是一個函數原本須要傳多個參數,經過柯里化的實現,每次只須要傳部分參數。
// say anyword to anyone
// 正常狀況是
function Hello(word, name) {
console.log(name + word);
}
Hello('hi','andy');複製代碼
function Hello(word) {
return function(name) {
console.log(name + word);
}
}
var sayHi = Hello('hi');
sayHi('Jack');複製代碼
深克隆,淺克隆
淺克隆和深克隆的區別就是,若是屬性值爲引用數據類型的時候,沒有改變指針;
function deepClone(obj) {
let copy;
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
copy = [];
for (let i = 0, len = obj.length; i < len; i++) {
copy[i] = deepClone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
copy = {};
for (let attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = deepClone(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}複製代碼
JS數據類型
基本數據類型:number String Boolean undefined null
引用數據類型:Object function Array Date RegExp
-
typeof方法能夠檢測number String Boolean undefined 這四種基本數據類型,還能夠檢測出object和function兩種引用數據類型
-
null, String(null)
-
檢測object的方法,Object.prototype.toString.call(obj).slice(8,-1).toLocaleLowerCase()
-
-、*、/、%:一概轉換成數值型再計算
-
+: 數字 + 字符串 = 字符串
var w = 1 + 'aaa' // "1aaa"複製代碼
-
. 數字 + 對象
var w = 2 + {a: 1}
// "2[object Object]"複製代碼
w = 1 + false
//1
w = 1 + true
//2
w = 1 + null
//1複製代碼
w = 1 + undefined
//NaN複製代碼
[1,2,3].toString()
//"1,2,3"
Object.prototype.toString.call({x:'a'})
//"[object Object]"
NaN !== NaN //true複製代碼
一些讀代碼的題目
var t = true;
window.setTimeout(function (){
t = false;
},1000);
while (t){}
alert('end');複製代碼
while(true){} 爲死循環,因此永遠沒有alert, 也不會執行setTimeout
var city = "Rome"+function() {
console.log(city);
var city = "Paris";
}();複製代碼
//undefined 執行console.log的時候,只有city的生命,尚未賦值;因此打印結果爲undefined 所謂變量的提高,是指變量聲明的提高,而變量賦值並無提高。
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function () {
return function () {
return this.name;
};
}
};
alert(object.getNameFunc()());複製代碼
// The Window
4.當點擊button#2的時候,console中會打印什麼?
var nodes = document.getElementsByTagName('button');
// assume there is totally 5 nodes
for (var i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', function() {
console.log('You clicked element #' + i);
});
}複製代碼
結果爲'you clicked element #5'。
最簡單的方式是將var 改爲let生成塊級做用域。
var parentClassEle = document.getElementsByClassName('parent');
for(var i = 0; i < parentClassEle.length; i++) {
addClick(i);
}
function addClick(i) {
var j = i;
parentClassEle[i].addEventListener('click', function() {
console.log(i)
})
}複製代碼
function Mammal(name) {
this.name = name;
this.offspring = [];
}
Mammal.prototype.haveABaby = function () {
var newBaby = new Mammal("Baby " + this.name);
this.offspring.push(newBaby);
return newBaby;
}
Mammal.prototype.toString = function () {
return '[Mammal "' + this.name + '"]';
}; // 到目前爲止,這 是一個Mammal對象的實現。
// 將Employee的原型指向Person的一個實例
// 由於Person的實例能夠調用Person原型中的方法, 因此Employee的實例也能夠調用Person原型中的全部屬性。
Cat.prototype = new Mammal();
//修正constructor
Cat.prototype.constructor = Cat;
function Cat(name) {
this.name = name;
}
Cat.prototype.toString = function () {
return '[Cat "' + this.name + '"]';
} // 到目前爲止,這是Mammal的一個子類Cat。
var someAnimal = new Mammal('Mr. Biggles');
var myPet = new Cat('Felix');
alert(someAnimal);
alert(myPet);
myPet.haveABaby();
alert(myPet.offspring.length);
alert(myPet.offspring[0]);複製代碼
一些手寫代碼題
1.寫一個方法,測試數組的每一個元素是否是比2大?
function ArrayItemBig2(arr) {
arr.every(val => {
return val > 2;
})
}複製代碼
function ArrayFilterBiger2(arr) {
var newArr = [];
arr.forEach(element => {
if(Number(element) > 2) {
newArr.push(element);
}
});
return newArr;
}複製代碼
和對象相關的題目:
1. 字面量
var obj = {}
2. new的方式
var obj = new Object()
3. 定義原型對象
var obj = Object.create({a: 1})
//obj.__proto__ = {a: 1}複製代碼
function Person(name) {
this.name = name;
}
Person.prototype = {
getName: function() {
console.log(thit.name)
}
}複製代碼
prototype是構造函數的一個屬性, 只有函數纔有prototype屬性;
__proto__是對象的一個屬性,每個Object都有__proto__屬性;
原型鏈是取決於__proto__的,而非prototype。複製代碼
例:
var child = new Person();
1.先建立一個空對象child = {}
2.將child.__proto__ = Person.prototype
3.將this指向改成child複製代碼
下邊能夠經過這個圖深入理解一下prototype、__proto__、 constructor、之間的關係
例:
child instanceof Person
爲了檢測構造函數的prototype屬性是否出如今該對象的原型鏈中複製代碼
做用域相關的問題: