JavaScript保姆級教程 ——— 重難點詳細決議(建議收藏)
- 1. JS函式
- 2. JS事件
- 3. JavaScript 物件
- 4. JavaScript prototype(原型物件)
- 5. call和apply及bind三者的區別(面試重點)
- 6. Javascript的事件流模型(面試重點)
- 7. 防抖與節流(面試精選)
- 8. JS中的虛擬DOM是什么?(面試重點)
- 9. 手寫一個new,實作同等功能
- 10. 獲得頁面url引數的值(常用)
- 本文是整理了JS中的一些重點,難點,以及不好理解的知識點
- 本文非常詳細,深入的講解,包學包會

1. JS函式
1.1 函式(Function)是什么?
- 函式(方法)是由事件驅動的或者當它被呼叫時執行的可重復使用的代碼塊 —— 官方說明
- 向來覺得官方的檔案是有些生硬的,舉個例子:
函式可以看做是功能(以一輛汽車為例,如下圖),這些都可以看做成是方法
- 剎車
- 油門
- 鳴笛
- 檔位

- 這些功能任何一個里面都有很多個零件的配合,共同完成某一個任務,我們只需要去呼叫(踩剎車,踩油門,按喇叭,掛擋),功能就會執行
- 函式也是一樣的,它內部封裝了一些操作,只有我們去呼叫的時候才會執行
1.2 一個最簡單的函式及觸發方法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>我的第一個方法</title>
</head>
<body>
<button onclick="myFunction()">點擊觸發函式</button>
<script>
// 必須有 function關鍵字,命名通常為駝峰命名,首字母小寫
function myFunction(){
alert("這是我的函式");
}
</script>
</body>
</html>
1.3 帶引數的函式(形參與實參)
- 形參 : 函式中定義的變數(此時是沒有值的,只是一個代稱)
- 實參 : 在運行時的函式呼叫時傳入的引數(實際的值)
- js中,方法中即使定義了形參,呼叫時不傳實參也不會報錯
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>形參與實參</title>
</head>
<body>
<!-- 這里的5和2是實參 -->
<button onclick="addNum(5, 2)">計算5+2的值</button>
<script>
// 此處的num1,與num2便是形參
function addNum(num1, num2){
alert(num1 + num2)
}
</script>
</body>
</html>
1.4 帶有回傳值的函式 ———— return
function fn(a, b){
return a*b;
}
// 呼叫并給num賦值
let num = fn(3, 5);
console.log(num) // 得到15
1.5 js函式內置物件 ———— arguments(重點,考點)
- 它是函式一創建就有的
- 是一個類陣列(并不是真正的陣列)
- 方法呼叫時,可以得到所有傳進來的引數
- 你傳多少,我就能拿到多少
function fn(){
console.log(arguments)
}
fn(1, 2, 3, 4);

經典應用 ———— 求一組引數的總和
function fn(){
let sum = 0;
for(let i = 0; i < arguments.length; i++){
sum += arguments[i];
}
// 回傳 sum
return sum
}
let allSum = fn(1, 2, 3, 4);
console.log(allSum) // 得到10
1.6 函式內的變數
- 在函式內的定義的變數均為區域變數
- 函式運行完之后就會銷毀(垃圾回識訓制),所以外界無法訪問
- 變數應盡量避免重名(區域與全域變數可能會混淆,導致一些意料之外的問題)
function fn() {
// 此為區域變數
let a = 5;
console.log(a)
}
fn();
console.log(a) // 此處報錯,無法訪問
1.7 匿名函式(難點)
- 顧名思義指的是沒有名字的函式
- 必須采用下面的語法,否則會報錯
(function (){
//由于沒有執行該匿名函式,所以不會執行匿名函式體內的陳述句,
console.log("666");
})
匿名自執行函式(類似于JS的單例模式)
(function (){
console.log("666"); // 此處會列印666
})()
2. JS事件
- HTML 事件是發生在 HTML 元素上的事情,
- JavaScript 可以觸發這些事件,
- 可以看做是用戶的某些操作,或者說業務需要監聽的某些操作
2.1 HTML事件
- HTML 頁面完成加載
- HTML input 欄位改變時
- HTML 按鈕被點擊
常用事件整理
| 事件名 | 說明 |
|---|---|
| onchange() | HTML 元素改變(一般用于表單元素) |
| onclick () | 用戶點擊 HTML 元素 |
| onmouseover() | 用戶在一個HTML元素上移動滑鼠 |
| onmouseout() | 用戶從一個HTML元素上移開滑鼠 |
| onkeydown() | 用戶按下鍵盤按鍵 |
| onkeyup() | 鍵盤按鍵彈起 |
| onload() | 瀏覽器已完成頁面的加載 |
2.2 JavaScript 事件一般用于做什么?
- 頁面加載時觸發事件
- 頁面關閉時觸發事件
- 用戶點擊按鈕執行動作
- 驗證用戶輸入內容的合法性
- …(用戶的一切操作都可以監聽)
2.3 事件實體
<input id="test" type="button" value="提交"/>
<script>
// 頁面加載完觸發
window.onload = function(){
let test = document.getElementById("test");
test.addEventListener("click",myfun2);
test.addEventListener("click",myfun1);
}
function myfun1(){
alert("你好1");
}
function myfun2(){
alert("你好2");
}
</script>
3. JavaScript 物件
在JS里 —— 萬物皆為物件

- 字串也可以是一個物件
- 日期是一個物件
- 數學和正則運算式也是物件
- 陣列是一個物件
- 函式也可以是物件
- …
3.1 物件定義
- 物件是變數的容器
- 寫法以鍵值對的方式(鍵名:鍵值)
- 鍵值對稱之為物件的屬性
- 回圈物件一般用 for in
// 物件定義
let person = {
firstName:"ouyang",
lastName:"xiu",
age:18
};
// 回圈物件
for(let key in person){
console.log(key); // 鍵名
console.log(person[key]) // 鍵值
}
3.2 大廠經典面試題分析
let obj = Object.create(null) 與 let obj = {} 有什么區別?
- 之前騰訊面試的時候,問了這個問題:物件字面量創建物件與 Object.create(null)創建物件有什么區別?
- 一開始是有點懵的,不都是創建物件么,能有啥不同,后面我去試了一下,結果發現還蠻有意思:
let obj = {};
let obj2 = Object.create(null);
console.log(obj);
console.log(obj2)
- 控制臺列印

- 乍一看,好像沒啥區別,都是一個花括號
- 然而,展開后,確實大有不同

- Object.create(null)創建的物件是非常純凈的,沒有任何其它元素
- 而另一個let創建的物件是帶有_proto_的,下面有一些方法與屬性,這便是js的原型鏈繼承,它繼承了Object的方法和屬性,這便是區別,
所以這種區別導致了使用場景不同
- 如果需要物件的繼承屬性和方法,那就使用 let obj = {};
- 如果只需要一個純凈的物件,那就使用 Object.create(null)
- 比如說,我只需要用物件來保存一些資料,然后進行回圈取用,提高回圈效率,
- 這個時候如果物件有原型鏈,那便會在回圈的時候去回圈它的各個屬性和方法
- 然而這不是必要的,我們只是要他里面的元素而已,前者會影響回圈效率

4. JavaScript prototype(原型物件)
- 此屬性是函式特有的
- 每個函式都會默認添加一個
- 用于繼承屬性和方法
// 創建建構式
function Person(name, age) {
this.age = age;
this.name= name;
this.fn = function(){
console.log(this.name)
}
}
// 創建實體
let person1 = new Person("小明", 18);
let person2 = new Person("小紅", 20);
person1.fn(); // 繼承父級的方法
person2.fn();
console.log(person1)
console.log(person2)
執行結果

- 要添加一個新的屬性需要在在構造器函式中添加:
function Person(name, age, sex) {
// sex為新屬性
this.sex = sex;
this.age = age;
this.name= name;
this.fn = function(){
console.log(this.name)
}
}
4.1 prototype 繼承
所有的 JavaScript 物件都會從一個 prototype(原型物件)中繼承屬性和方法:
- Date 物件從 Date.prototype 繼承
- Array 物件從 Array.prototype 繼承
- Person 物件從 Person.prototype 繼承
所有 JavaScript 中的物件都是位于原型鏈頂端的 Object 的實體
- JavaScript 物件有一個指向一個原型物件的鏈
- 當試圖訪問一個物件的屬性時,它不僅僅在該物件上搜尋,還會搜尋該物件的原型,以及該物件的原型的原型,依次層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾(逐級查找)
- Date 物件, Array 物件, 以及 Person 物件從 Object.prototype 繼承,
4.2 添加屬性和方法
function Person(name, age, sex) {
// sex為新屬性
this.sex = sex;
this.age = age;
this.name= name;
this.fn = function(){
console.log(this.name)
}
}
Person.prototype.newVal = "我是新添加在原型上的值";
let person1 = new Person("小明", 18);
console.log(person1)
- 一樣可以通過繼承拿到

5. call和apply及bind三者的區別(面試重點)
- this指向,apply,call,bind的區別是一個經典的面試問題
- 同時在專案中會經常使用到的原生的js方法,
- 也是ES5中的眾多坑的一個
5.1 從this說起
- this指向 = 誰呼叫,指向誰(這是錯誤的!!!)
- this永遠指向最后一個呼叫它的那個物件(正解)
如何解決this指向問題?

-
使用ES6中箭頭函式
-
函式內部使用_this = this
-
使用apply,call,bind方法
-
new實體化一個物件
5.2 談談apply,call,bind
- apply()
let obj = {
name : "小明",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.apply(name),1000);
}
};
obj.func2() // 小明
-
apply() 方法呼叫一個函式,其具有一個指定的this值,以及作為一個陣列(或者類似陣列的物件)提供的引數,fun.apply(thisArg, [argsArray])
-
thisArg:在fun函式運行時指定的this值,指定this的值并不一定是函式執行時真正的this值,如果是原始值的this會指向該原始值的自動包裝物件,
-
argsArray:一個陣列或者類陣列物件,其中的陣列元素將作為單獨的引數傳給fun函式,引數為null或者undefined,則表示不需要傳入任何引數,
- call()
let obj2 = {
name : "小紅",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.call(name),1000);
}
};
obj2.func2() // 小紅
-
call() 呼叫一個函式,其具有一個指定的this值,以及若干個引數串列,fun.call(thisArg, arg1, arg2, …)
-
thisArg:在fun函式運行時指定的this值,指定this的值并不一定是函式執行時真正的this值,如果是原始值的this會指向該原始值的自動包裝物件,
-
arg1, arg2, …:若干個引數串列
- bind()
let obj3 = {
name : "小豬",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.bind(name)(),1000);
}
};
obj3.func2() // 小豬
-
bind() 創建一個新的函式,當被呼叫時,將其this的關鍵字設定為提供的值,在呼叫新函式時,在任何提供一個給定的引數序列,
-
bind創建了一個新函式,必須手動去呼叫,
5.3 區別
- apply和call基本類似,他們的區別只是傳入的引數不同,
- apply傳入的引數是包含多個引數的陣列
- call傳入的引數是若干個引數串列
- bind方法會創建一個新的函式,當被呼叫的時候,將其this關鍵字設定為提供的值,我們必須手動去呼叫

6. Javascript的事件流模型(面試重點)
-
事件冒泡:事件開始由最具體的元素接受,然后逐級向上傳播

-
事件捕捉:事件由最不具體的節點先接收,然后逐級向下,一直到最具體的(與上面相反)
-
DOM事件流:三個階段:事件捕捉,目標階段,事件冒泡
7. 防抖與節流(面試精選)
7.1 函式防抖
- 當持續觸發事件時,一段時間內只能觸發一次,將幾次操作合并為一此操作進行,比如說有一條賽車通道,賽車通過的時間為5s,5s之后到達終點,執行領獎操作

- 這5s之內只允許一輛賽車在通道內,如果第一輛賽車還在通道內,此時第二輛賽車已經進來了,那么銷毀第一輛賽車,從第二輛車入場重新計時5s執行領獎操作

應用場景(資料抖動問題)
let telInput = document.querySelector('input');
telInput.addEventListener('input', function(e) {
//如果直接每次發請求,會導致性能問題
//資料請求
let timeOut = null;
if(timeOut){
clearTimeout(timeOut)
}else{
timeOut = setTimeout(()=>{
$.ajax({})
},2000)
}
})
7.2 函式節流
- 當持續觸發事件時,保證一定時間段內只呼叫一次事件處理函式,節流,顧名思義,節制流入或流出,
- 比如說水龍頭放水,一旦打開開關,水流就會很快,我們要做的就是限制流出
應用場景(客運站問題)
-
把整個事件處理器比喻成客運站,如果客運大巴到站就走,那么路上肯定會發生交通擁堵,而且車大部分是空的
-
因為沒給時間上客,虛假繁忙的情況肯定是不好的,那么怎么處理呢?
-
設定一個時間間隔,時間間隔內只允許執行一次,客運站大巴設定一個時間,到點才會走
let throttle = function(func, delay) {
let prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function demo() {
//do something
//ajax({})
//...
}
box.addEventListener('touchmove', throttle(demo, 2000));
8. JS中的虛擬DOM是什么?(面試重點)

8.1 為什么要有虛擬dom?
- 檔案物件模型或 DOM 定義了一個介面,該介面允許 JavaScript 之類的語言訪問和操作 HTML 檔案
- 但是此介面需要付出代價,大量非常頻繁的 DOM 操作會使頁面速度變慢
- 虛擬dom的出現就是為了解決操作dom的性能問題
8.2 虛擬dom是什么?好處是?
- 本質就是JS物件
- 真實節點抽象成JS物件(檔案結構樹)
- 虛擬節點(VNode)表示 DOM 樹中的節點,當需要操縱時,可以在虛擬 DOM的 記憶體中執行計算和操作,而不是在真實 DOM 上進行操縱,
- 相對于直接操作dom,這自然會更快
9. 手寫一個new,實作同等功能
function Person(name) {
this.name = name
this.sayName= function () {
console.log(`我是 ${this.name}!`)
}
}
function myNew(that, ...args) {
const obj = Object.create(null)
obj.__proto__ = that.prototype
const res = that.call(obj, ...args)
return res instanceof Object ? res : obj
}
let person= myNew(Person, '小明')
person.sayWorld(); // 我是小明
10. 獲得頁面url引數的值(常用)
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
}
1. 希望本文能對大家有所幫助,如有錯誤,敬請指出
2. 原創不易,還請各位客官動動發財的小手支持一波(關注、評論、點贊、收藏)
3. 拜謝各位!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/287750.html
標籤:其他
上一篇:js初識+簡單案例
下一篇:Web前端-JS基礎
