- 本文是整理了JS中的一些重點(diǎn),難點(diǎn),以及不好理解的知識(shí)點(diǎn)
-
本文非常詳細(xì),深入的講解,包學(xué)包會(huì)
1. JS函數(shù)
1.1 函數(shù)(Function)是什么?
- 函數(shù)(方法)是由事件驅(qū)動(dòng)的或者當(dāng)它被調(diào)用時(shí)執(zhí)行的可重復(fù)使用的代碼塊 —— 官方說明
- 向來覺得官方的文檔是有些生硬的,舉個(gè)例子:
函數(shù)可以看做是功能(以一輛汽車為例,如下圖),這些都可以看做成是方法
- 剎車
- 油門
- 鳴笛
- 檔位
- 這些功能任何一個(gè)里面都有很多個(gè)零件的配合,共同完成某一個(gè)任務(wù),我們只需要去調(diào)用(踩剎車,踩油門,按喇叭,掛擋),功能就會(huì)執(zhí)行
- 函數(shù)也是一樣的,它內(nèi)部封裝了一些操作,只有我們?nèi)フ{(diào)用的時(shí)候才會(huì)執(zhí)行
1.2 一個(gè)最簡(jiǎn)單的函數(shù)及觸發(fā)方法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>我的第一個(gè)方法</title>
</head>
<body>
<button onclick="myFunction()">點(diǎn)擊觸發(fā)函數(shù)</button>
<script>
// 必須有 function關(guān)鍵字,命名通常為駝峰命名,首字母小寫
function myFunction(){
alert("這是我的函數(shù)");
}
</script>
</body>
</html>
1.3 帶參數(shù)的函數(shù)(形參與實(shí)參)
- 形參 : 函數(shù)中定義的變量(此時(shí)是沒有值的,只是一個(gè)代稱)
- 實(shí)參 : 在運(yùn)行時(shí)的函數(shù)調(diào)用時(shí)傳入的參數(shù)(實(shí)際的值)
- js中,方法中即使定義了形參,調(diào)用時(shí)不傳實(shí)參也不會(huì)報(bào)錯(cuò)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>形參與實(shí)參</title>
</head>
<body>
<!-- 這里的5和2是實(shí)參 -->
<button onclick="addNum(5, 2)">計(jì)算5+2的值</button>
<script>
// 此處的num1,與num2便是形參
function addNum(num1, num2){
alert(num1 + num2)
}
</script>
</body>
</html>
1.4 帶有返回值的函數(shù) ———— return
function fn(a, b){
return a*b;
}
// 調(diào)用并給num賦值
let num = fn(3, 5);
console.log(num) // 得到15
1.5 js函數(shù)內(nèi)置對(duì)象 ———— arguments(重點(diǎn),考點(diǎn))
- 它是函數(shù)一創(chuàng)建就有的
- 是一個(gè)類數(shù)組(并不是真正的數(shù)組)
- 方法調(diào)用時(shí),可以得到所有傳進(jìn)來的參數(shù)
- 你傳多少,我就能拿到多少
function fn(){
console.log(arguments)
}
fn(1, 2, 3, 4);
經(jīng)典應(yīng)用 ———— 求一組參數(shù)的總和
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 函數(shù)內(nèi)的變量
- 在函數(shù)內(nèi)的定義的變量均為局部變量
- 函數(shù)運(yùn)行完之后就會(huì)銷毀(垃圾回收機(jī)制),所以外界無法訪問
- 變量應(yīng)盡量避免重名(局部與全局變量可能會(huì)混淆,導(dǎo)致一些意料之外的問題)
function fn() {
// 此為局部變量
let a = 5;
console.log(a)
}
fn();
console.log(a) // 此處報(bào)錯(cuò),無法訪問
1.7 匿名函數(shù)(難點(diǎn))
- 顧名思義指的是沒有名字的函數(shù)
- 必須采用下面的語法,否則會(huì)報(bào)錯(cuò)
(function (){
//由于沒有執(zhí)行該匿名函數(shù),所以不會(huì)執(zhí)行匿名函數(shù)體內(nèi)的語句。
console.log("666");
})
匿名自執(zhí)行函數(shù)(類似于JS的單例模式)
(function (){
console.log("666"); // 此處會(huì)打印666
})()
2. JS事件
- HTML 事件是發(fā)生在 HTML 元素上的事情。
- JavaScript 可以觸發(fā)這些事件。
- 可以看做是用戶的某些操作,或者說業(yè)務(wù)需要監(jiān)聽的某些操作
2.1 HTML事件
- HTML 頁(yè)面完成加載
- HTML input 字段改變時(shí)
- HTML 按鈕被點(diǎn)擊
常用事件整理
事件名 | 說明 |
---|---|
onchange() | HTML 元素改變(一般用于表單元素) |
onclick () | 用戶點(diǎn)擊 HTML 元素 |
onmouseover() | 用戶在一個(gè)HTML元素上移動(dòng)鼠標(biāo) |
onmouseout() | 用戶從一個(gè)HTML元素上移開鼠標(biāo) |
onkeydown() | 用戶按下鍵盤按鍵 |
onkeyup() | 鍵盤按鍵彈起 |
onload() | 瀏覽器已完成頁(yè)面的加載 |
2.2 JavaScript 事件一般用于做什么?
- 頁(yè)面加載時(shí)觸發(fā)事件
- 頁(yè)面關(guān)閉時(shí)觸發(fā)事件
- 用戶點(diǎn)擊按鈕執(zhí)行動(dòng)作
- 驗(yàn)證用戶輸入內(nèi)容的合法性
- …(用戶的一切操作都可以監(jiān)聽)
2.3 事件實(shí)例
<input id="test" type="button" value="提交"/>
<script>
// 頁(yè)面加載完觸發(fā)
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 對(duì)象
在JS里 —— 萬物皆為對(duì)象
- 字符串也可以是一個(gè)對(duì)象
- 日期是一個(gè)對(duì)象
- 數(shù)學(xué)和正則表達(dá)式也是對(duì)象
- 數(shù)組是一個(gè)對(duì)象
- 函數(shù)也可以是對(duì)象
- …
3.1 對(duì)象定義
- 對(duì)象是變量的容器
- 寫法以鍵值對(duì)的方式(鍵名:鍵值)
- 鍵值對(duì)稱之為對(duì)象的屬性
- 循環(huán)對(duì)象一般用 for in
// 對(duì)象定義
let person = {
firstName:"ouyang",
lastName:"xiu",
age:18
};
// 循環(huán)對(duì)象
for(let key in person){
console.log(key); // 鍵名
console.log(person[key]) // 鍵值
}
3.2 大廠經(jīng)典面試題分析
let obj = Object.create(null) 與 let obj = {} 有什么區(qū)別?
- 之前騰訊面試的時(shí)候,問了這個(gè)問題:對(duì)象字面量創(chuàng)建對(duì)象與 Object.create(null)創(chuàng)建對(duì)象有什么區(qū)別?
- 一開始是有點(diǎn)懵的,不都是創(chuàng)建對(duì)象么,能有啥不同,后面我去試了一下,結(jié)果發(fā)現(xiàn)還蠻有意思:
let obj = {};
let obj2 = Object.create(null);
console.log(obj);
console.log(obj2)
- 控制臺(tái)打印
- 乍一看,好像沒啥區(qū)別,都是一個(gè)花括號(hào)
- 然而,展開后,確實(shí)大有不同
- Object.create(null)創(chuàng)建的對(duì)象是非常純凈的,沒有任何其它元素
- 而另一個(gè)let創(chuàng)建的對(duì)象是帶有_proto_的,下面有一些方法與屬性,這便是js的原型鏈繼承,它繼承了Object的方法和屬性。這便是區(qū)別。
所以這種區(qū)別導(dǎo)致了使用場(chǎng)景不同
- 如果需要對(duì)象的繼承屬性和方法,那就使用 let obj = {};
- 如果只需要一個(gè)純凈的對(duì)象,那就使用 Object.create(null)
- 比如說,我只需要用對(duì)象來保存一些數(shù)據(jù),然后進(jìn)行循環(huán)取用,提高循環(huán)效率。
- 這個(gè)時(shí)候如果對(duì)象有原型鏈,那便會(huì)在循環(huán)的時(shí)候去循環(huán)它的各個(gè)屬性和方法
- 然而這不是必要的,我們只是要他里面的元素而已,前者會(huì)影響循環(huán)效率
4. JavaScript prototype(原型對(duì)象)
- 此屬性是函數(shù)特有的
- 每個(gè)函數(shù)都會(huì)默認(rèn)添加一個(gè)
- 用于繼承屬性和方法
// 創(chuàng)建構(gòu)造函數(shù)
function Person(name, age) {
this.age = age;
this.name= name;
this.fn = function(){
console.log(this.name)
}
}
// 創(chuàng)建實(shí)例
let person1 = new Person("小明", 18);
let person2 = new Person("小紅", 20);
person1.fn(); // 繼承父級(jí)的方法
person2.fn();
console.log(person1)
console.log(person2)
執(zhí)行結(jié)果
- 要添加一個(gè)新的屬性需要在在構(gòu)造器函數(shù)中添加:
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 對(duì)象都會(huì)從一個(gè) prototype(原型對(duì)象)中繼承屬性和方法:
- Date 對(duì)象從 Date.prototype 繼承
- Array 對(duì)象從 Array.prototype 繼承
- Person 對(duì)象從 Person.prototype 繼承
所有 JavaScript 中的對(duì)象都是位于原型鏈頂端的 Object 的實(shí)例
- JavaScript 對(duì)象有一個(gè)指向一個(gè)原型對(duì)象的鏈
- 當(dāng)試圖訪問一個(gè)對(duì)象的屬性時(shí),它不僅僅在該對(duì)象上搜尋,還會(huì)搜尋該對(duì)象的原型,以及該對(duì)象的原型的原型,依次層層向上搜索,直到找到一個(gè)名字匹配的屬性或到達(dá)原型鏈的末尾(逐級(jí)查找)
- Date 對(duì)象, Array 對(duì)象, 以及 Person 對(duì)象從 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三者的區(qū)別(面試重點(diǎn))
- this指向,apply,call,bind的區(qū)別是一個(gè)經(jīng)典的面試問題
- 同時(shí)在項(xiàng)目中會(huì)經(jīng)常使用到的原生的js方法。
- 也是ES5中的眾多坑的一個(gè)
5.1 從this說起
- this指向 = 誰調(diào)用,指向誰(這是錯(cuò)誤的?。?!)
- this永遠(yuǎn)指向最后一個(gè)調(diào)用它的那個(gè)對(duì)象(正解)
如何解決this指向問題?
-
使用ES6中箭頭函數(shù)
-
函數(shù)內(nèi)部使用_this = this
-
使用apply,call,bind方法
-
new實(shí)例化一個(gè)對(duì)象
5.2 談?wù)刟pply,call,bind
- apply()
let obj = {
name : "小明",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.apply(name),1000);
}
};
obj.func2() // 小明
-
apply() 方法調(diào)用一個(gè)函數(shù),其具有一個(gè)指定的this值,以及作為一個(gè)數(shù)組(或者類似數(shù)組的對(duì)象)提供的參數(shù),fun.apply(thisArg, [argsArray])
-
thisArg:在fun函數(shù)運(yùn)行時(shí)指定的this值。指定this的值并不一定是函數(shù)執(zhí)行時(shí)真正的this值,如果是原始值的this會(huì)指向該原始值的自動(dòng)包裝對(duì)象。
-
argsArray:一個(gè)數(shù)組或者類數(shù)組對(duì)象,其中的數(shù)組元素將作為單獨(dú)的參數(shù)傳給fun函數(shù)。參數(shù)為null或者undefined,則表示不需要傳入任何參數(shù)。
- call()
let obj2 = {
name : "小紅",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.call(name),1000);
}
};
obj2.func2() // 小紅
-
call() 調(diào)用一個(gè)函數(shù),其具有一個(gè)指定的this值,以及若干個(gè)參數(shù)列表,fun.call(thisArg, arg1, arg2, …)
-
thisArg:在fun函數(shù)運(yùn)行時(shí)指定的this值。指定this的值并不一定是函數(shù)執(zhí)行時(shí)真正的this值,如果是原始值的this會(huì)指向該原始值的自動(dòng)包裝對(duì)象。
-
arg1, arg2, …:若干個(gè)參數(shù)列表
- bind()
let obj3 = {
name : "小豬",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.bind(name)(),1000);
}
};
obj3.func2() // 小豬
-
bind() 創(chuàng)建一個(gè)新的函數(shù),當(dāng)被調(diào)用時(shí),將其this的關(guān)鍵字設(shè)置為提供的值,在調(diào)用新函數(shù)時(shí),在任何提供一個(gè)給定的參數(shù)序列。
-
bind創(chuàng)建了一個(gè)新函數(shù),必須手動(dòng)去調(diào)用。
5.3 區(qū)別
- apply和call基本類似,他們的區(qū)別只是傳入的參數(shù)不同。
- apply傳入的參數(shù)是包含多個(gè)參數(shù)的數(shù)組
- call傳入的參數(shù)是若干個(gè)參數(shù)列表
- bind方法會(huì)創(chuàng)建一個(gè)新的函數(shù),當(dāng)被調(diào)用的時(shí)候,將其this關(guān)鍵字設(shè)置為提供的值,我們必須手動(dòng)去調(diào)用
6. Javascript的事件流模型(面試重點(diǎn))
-
事件冒泡:事件開始由最具體的元素接受,然后逐級(jí)向上傳播
-
事件捕捉:事件由最不具體的節(jié)點(diǎn)先接收,然后逐級(jí)向下,一直到最具體的(與上面相反)
-
DOM事件流:三個(gè)階段:事件捕捉,目標(biāo)階段,事件冒泡
7. 防抖與節(jié)流(面試精選)
7.1 函數(shù)防抖
- 當(dāng)持續(xù)觸發(fā)事件時(shí),一段時(shí)間內(nèi)只能觸發(fā)一次。將幾次操作合并為一此操作進(jìn)行。比如說有一條賽車通道,賽車通過的時(shí)間為5s,5s之后到達(dá)終點(diǎn),執(zhí)行領(lǐng)獎(jiǎng)操作
- 這5s之內(nèi)只允許一輛賽車在通道內(nèi),如果第一輛賽車還在通道內(nèi),此時(shí)第二輛賽車已經(jīng)進(jìn)來了,那么銷毀第一輛賽車,從第二輛車入場(chǎng)重新計(jì)時(shí)5s執(zhí)行領(lǐng)獎(jiǎng)操作
應(yīng)用場(chǎng)景(數(shù)據(jù)抖動(dòng)問題)
let telInput = document.querySelector('input');
telInput.addEventListener('input', function(e) {
//如果直接每次發(fā)請(qǐng)求,會(huì)導(dǎo)致性能問題
//數(shù)據(jù)請(qǐng)求
let timeOut = null;
if(timeOut){
clearTimeout(timeOut)
}else{
timeOut = setTimeout(()=>{
$.ajax({})
},2000)
}
})
7.2 函數(shù)節(jié)流
- 當(dāng)持續(xù)觸發(fā)事件時(shí),保證一定時(shí)間段內(nèi)只調(diào)用一次事件處理函數(shù)。節(jié)流,顧名思義,節(jié)制流入或流出。
- 比如說水龍頭放水,一旦打開開關(guān),水流就會(huì)很快,我們要做的就是限制流出
應(yīng)用場(chǎng)景(客運(yùn)站問題)
-
把整個(gè)事件處理器比喻成客運(yùn)站,如果客運(yùn)大巴到站就走,那么路上肯定會(huì)發(fā)生交通擁堵,而且車大部分是空的
-
因?yàn)闆]給時(shí)間上客,虛假繁忙的情況肯定是不好的,那么怎么處理呢?
-
設(shè)置一個(gè)時(shí)間間隔,時(shí)間間隔內(nèi)只允許執(zhí)行一次,客運(yùn)站大巴設(shè)定一個(gè)時(shí)間,到點(diǎn)才會(huì)走
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是什么?(面試重點(diǎn))
8.1 為什么要有虛擬dom?
- 文檔對(duì)象模型或 DOM 定義了一個(gè)接口,該接口允許 JavaScript 之類的語言訪問和操作 HTML 文檔
- 但是此接口需要付出代價(jià),大量非常頻繁的 DOM 操作會(huì)使頁(yè)面速度變慢
- 虛擬dom的出現(xiàn)就是為了解決操作dom的性能問題
8.2 虛擬dom是什么?好處是?
- 本質(zhì)就是JS對(duì)象
- 真實(shí)節(jié)點(diǎn)抽象成JS對(duì)象(文檔結(jié)構(gòu)樹)
- 虛擬節(jié)點(diǎn)(VNode)表示 DOM 樹中的節(jié)點(diǎn)。當(dāng)需要操縱時(shí),可以在虛擬 DOM的 內(nèi)存中執(zhí)行計(jì)算和操作,而不是在真實(shí) DOM 上進(jìn)行操縱。
- 相對(duì)于直接操作dom,這自然會(huì)更快
9. 手寫一個(gè)new,實(shí)現(xiàn)同等功能
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. 獲得頁(yè)面url參數(shù)的值(常用)
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. 希望本文能對(duì)大家有所幫助,如有錯(cuò)誤,敬請(qǐng)指出
2. 原創(chuàng)不易,還請(qǐng)各位客官動(dòng)動(dòng)發(fā)財(cái)?shù)男∈种С忠徊ǎP(guān)注、評(píng)論、點(diǎn)贊、收藏)
3. 拜謝各位!文章來源:http://www.zghlxwxcb.cn/news/detail-405236.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-405236.html
到了這里,關(guān)于JavaScript保姆級(jí)教程 ——— 重難點(diǎn)詳細(xì)解析(萬字長(zhǎng)文,建議收藏)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!