大前端面試梳理
HTML
CSS
Javascript
JS基本介紹
- JS的用途:Javascript可以實現瀏覽器端、伺服器端(nodejs)。。。
-
瀏覽器端JS由以下三個部分組成:
- ECMAScript:基礎語法(資料型別、運算子、函式。。。)
- BOM(瀏覽器物件模型):window、location、history、navigator。。。
- DOM(文件物件模型):div、p、span。。。
-
ECMAScript又名es,有以下重大版本:
-
舊時代:
- es1.0。。。es3.1
-
新時代:
- es5
- es6(es2015)
- es7(es2016)、es8(es2017)
-
資料型別
-
基本資料型別——值型別:(數字、字串、布林值、null、undefined)
- undefined型別?
-
複雜資料型別——引用型別:(物件)
- 陣列
- 函式
- 正則表示式
- Date
物件的基本使用
建立一個物件
var student={ name:"李白" , //student有一個name屬性,值為"李白" grade:"初一" , //a、student有一個say屬性,值為一個函式 //b、student有一個say方法 say:function(){ console.log("你好"); }, run:function(speed){ console.log("正在以"+speed+"米/秒的速度奔跑"); } }
物件是鍵值對的集合:物件是由屬性和方法構成的 (ps:也有說法為:物件裡面皆屬性,認為方法也是一個屬性)
- name是屬性grade是屬性
- say是方法run是方法
物件屬性操作
獲取屬性:
第一種方式:.語法
- student.name獲取到name屬性的值,為:"李白"
- student.say獲取到一個函式
第二種方式:[]語法
- student["name"]等價於student.name
- student["say"]等價於student.say
2種方式的差異:
-
.語法更方便,但是坑比較多(有侷限性),比如:
- .後面不能使用js中的關鍵字、保留字(class、this、function。。。)
- .後面不能使用數字
var obj={}; obj.this=5; //語法錯誤 obj.0=10;//語法錯誤
-
[]使用更廣泛
- o1[變數name]
-
["class"]、["this"]都可以隨意使用
obj["this"]=10
-
[0]、[1]、[2]也可以使用
obj[3]=50 = obj["3"]=50
-
甚至還可以這樣用:["[object Array]"]
- jquery裡面就有這樣的實現
-
也可以這樣用:["{abc}"]
- 給物件添加了{abc}屬性
設定屬性
-
student["gender"]="男"
等價於:student.gender="男"
- 含義:如果student物件中沒有gender屬性,就新增一個gender屬性,值為"男"
- 如果student物件中有gender屬性,就修改gender屬性的值為"男"
-
案例1:
student.isFemale=true
-
案例2:
student["children"]=[1,2,5]
- 案例3:
student.toShanghai=function(){ console.log("正在去往上海的路上") }
刪除屬性
- delete student["gender"]
- delete student.gender
通過建構函式建立物件
建構函式建立物件的例子:
- var xiaoming = new Object()-->var xiaoming = {};
- var now = new Date()
- var rooms = new Array(1,3,5)-->var rooms = [1,3,5]
-
var isMale=/123/;
==>var isMale=new RegExp("123")
- isMale是通過RegExp建構函式創建出來的物件
- isMale是RegExp建構函式的例項
- 以上例子中,Object、Date、Array都是內建的建構函式
自定義一個建構函式來建立物件
- 建構函式
function Person(name,age){ this.name=name; this.age=age; } var p1=new Person("趙雲",18)
-
說明:
p1就是根據【Person建構函式】創建出來的物件
建構函式的概念
-
任何函式都可以當成建構函式
function CreateFunc(){ }
-
只要把一個函式通過new的方式來進行呼叫,我們就把這一次函式的呼叫方式稱之為:建構函式的呼叫
- new CreateFunc(); 此時CreateFunc就是一個建構函式
- CreateFunc();此時的CreateFunc並不是建構函式
關於new Object()
- new Object()等同於物件字面量{}
建構函式的執行過程
var p1=new Person();
-
1、建立一個物件 (我們把這個物件稱之為Person建構函式的例項)-
_p1
-
2、建立一個內部物件,
this
,將this指向該例項(_p1) - 3、執行函式內部的程式碼,其中,操作this的部分就是操作了該例項(_p1)
-
4、返回值:
- a、如果函式沒有返回值(沒有return語句),那麼就會返回建構函式的例項(p1)
- b、如果函式返回了一個基本資料型別的值,那麼本次建構函式的返回值是該例項(_p1)
function fn(){ } var f1=new fn();//f1就是fn的例項 function fn2(){ return "abc"; } var f2=new fn2();//f2是fn2建構函式的例項
- c、如果函式返回了一個複雜資料型別的值,那麼本次函式的返回值就是該值
function fn3(){ return [1,3,5]; //陣列是一個物件型別的值, //所以陣列是一個複雜資料型別的值 //-->本次建構函式的真正返回值就是該陣列 //-->不再是fn3建構函式的例項 } var f3=new fn3();//f3還是fn3的例項嗎?錯 //f3值為[1,3,5]
繼承
JS中繼承的概念:
-
通過【某種方式】讓一個物件可以訪問到另一個物件中的屬性和方法,我們把這種方式稱之為繼承
並不是所謂的xxx extends yyy
為什麼要使用繼承?
- 有些物件會有方法(動作、行為),而這些方法都是函式,如果把這些方法和函式都放在建構函式中宣告就會導致記憶體的浪費
function Person(){ this.say=function(){ console.log("你好") } } var p1=new Person(); var p2=new Person(); console.log(p1.say === p2.say);//false
繼承的第一種方式:原型鏈繼承1
Person.prototype.say=function(){ console.log("你好") }
- 缺點:新增1、2個方法無所謂,但是如果方法很多會導致過多的程式碼冗餘
繼承的第二種方式:原型鏈繼承2
Person.prototype={ constructor:Person, say:function(){ console.log("你好"); }, run:function(){ console.log("正在進行百米衝刺"); } }
- 注意點:
- a、一般情況下,應該先改變原型物件,再建立物件
- b、一般情況下,對於新原型,會新增一個constructor屬性,從而不破壞原有的原型物件的結構
繼承的第三種方式:拷貝繼承(混入繼承)
- 場景:有時候想使用某個物件中的屬性,但是又不能直接修改它,於是就可以建立一個該物件的拷貝
- 實現1:
var source={name:"李白",age:15} var target={}; target.name=source.name target.age=source.age;
- 上面的方式很明顯無法重用,實際程式碼編寫過程中,很多時候都會使用拷貝繼承的方式,所以為了重用,可以編寫一個函式把他們封裝起來:
function extend(target,source){ for(key in source){ target[key]=source[key]; } return target; } extend(target,source)
-
由於拷貝繼承在實際開發中使用場景非常多,所以很多庫都對此有了實現
- jquery:$.extend
- es6中有了物件擴充套件運算子彷彿就是專門為了拷貝繼承而生:
var source={name:"李白",age:15} var target={ ...source }
繼承的第四種方式:原型式繼承
-
場景:
- 建立一個純潔的物件
- 建立一個繼承自某個父物件的子物件
-
使用方式:
- 空物件:Object.create(null)
var o1={ say:function(){} } var o2=Object.create(o1);
繼承的第五種方式:借用建構函式實現繼承
- 場景:適用於2種建構函式之間邏輯有相似的情況
function Animal(name){
this.name=name;
}
function Person(name,age){
this.name=name; this.age=age;
}
- 以上程式碼用借用建構函式實現
function Animal(name,age){ this.name=name; this.age=age; } function Person(name,age,address){ Animal.call(this,name); //this.name=name; //this.age=age; this.address=address; }
原型鏈
閉包
變數作用域
- 變數作用域的概念:就是一個變數可以使用的範圍
- JS中首先有一個最外層的作用域:稱之為全域性作用域
- JS中還可以通過函式創建出一個獨立的作用域,其中函式可以巢狀,所以作用域也可以巢狀
作用域鏈
- 由於作用域是相對於變數而言的,而如果存在多級作用域,這個變數又來自於哪裡?這個問題就需要好好地探究一下了,我們把這個變數的查詢過程稱之為變數的作用域鏈
-
簡單來說,作用域鏈可以用以下幾句話來概括:(或者說:確定一個變數來自於哪個作用域)
-
檢視當前作用域,如果當前作用域聲明瞭這個變數,就確定結果
-
查詢當前作用域的上級作用域,也就是當前函式的上級函式,看看上級函式中有沒有宣告
-
再查詢上級函式的上級函式,直到全域性作用域為止
- 如果全域性作用域中也沒有,我們就認為這個變數未宣告(xxx is not defined)
-
-
-
- 舉例1:
var name="張三"; function f1(){ var name="abc"; console.log(name); } f1();
- 舉例2:
var name="張三"; function f1(){ console.log(name); var name="abc"; } f1();
- 舉例3:
var name="張三"; function f1(){ console.log(name); var name="abc"; } f1();
- 舉例4:
var name="張三"; function f1(){ return function(){ console.log(name); } var name="abc"; } var fn=f1(); fn();
- 舉例5:
var name="張三"; function f1(){ return { say:function(){ console.log(name); var name="abc"; } } } var fn=f1();
閉包的問題
function fn(){ var a=5; return function(){ a++; console.log(a); } } var f1=fn(); f1(); f1(); f1();
閉包問題的產生原因
- 函式執行完畢後,作用域中保留了最新的a變數的值
閉包的應用場景
- 模組化
- 防止變數被破壞
es6內容
- 1、解構賦值
- 2、函式rest引數
-
3、箭頭函式
- 箭頭函式和普通函式有哪些不同?(4點)
- 4、物件的Object.assign
- 5、promise
- 6、generator
- 7、async
- 8、class
- 9、module
原型
-
原型很多人開發用不到?
- 很多人都用es6/7/8開發,確實用的比較少
- 如果你用es5之前的版本開發程式碼(IE8、IE7。。。),可能天天都要寫原型
- 理解了原型,才是理解了JS面向物件的核心,沒有理解原型,你就沒有理解面向物件的核心