函式的三種呼叫方式(this指向)
函式三種呼叫方式:普通函式 物件方法 建構式
重點:理解this關鍵字的作用:誰呼叫這個函式,this就指向誰
無法修改this指向的三種:
1.普通函式呼叫:this指向的就是window
2.物件方法呼叫:this指向的就是這個物件
3.建構式呼叫:this指向的就是new關鍵字創造的物件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/*
函式三種執行模式 : 全域函式 、 物件方法 、 建構式
this : 誰 `呼叫` 我,我就指向誰
1. 全域函式 : this指向window
2. 物件方法 : this指向物件
3. 建構式 : this指向new創建的空物件
*/
// this環境物件:誰呼叫我,我就指向誰
// 普通函式呼叫 this指向的就是window
function fn() {
console.log(this);
}
fn()
// 物件方法呼叫:物件名.方法名() this指向就是這個物件
let obj = {
name: '陳爽',
age: 19,
saiHia: function () {
console.log(this);
}
}
obj.saiHia()
// 建構式呼叫:new 函式名() this指向new 創建的實體物件
function Fn() {
console.log(this);
}
let s = new Fn()
</script>
</body>
</html>
函式呼叫的背景關系模式(可以修改函式中this指向)
函式背景關系的三個方法:call() 、 apply() 、 bind() ,他們定義在Function建構式中
函式執行背景關系模
作用可以修改this指向
異同點:都可以修改函式中的this指向
不同點:傳參方式不同
call:函式名.call(this修改后的指向,引數1,引數2....)
適用于只有一個引數的函式
應用場景:偽陣列排序、檢測資料型別
// call可以修改this指向
// 語法:函式名.call(修改的this,引數1,引數2)
function fn(a, b) {
console.log(this);
console.log(a + b);
}
fn.call({ name: '張子沁' }, 30, 20)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 檢測資料型別固定格式語法:object.prototype.toString.call(資料)
console.log(Object.prototype.toString.call('123'));
console.log(Object.prototype.toString.call(12312));
console.log(Object.prototype.toString.call(true));
console.log(Object.prototype.toString.call(undefined));
console.log(Object.prototype.toString.call(null));
console.log(Object.prototype.toString.call([10, 20, 30]));
console.log(Object.prototype.toString.call(function () { }));
console.log(Object.prototype.toString.call({ name: '張三' }));
</script>
</body>
</html>
apply() :函式名:apply(this修改后的指向,偽陣列/陣列)
適用于多個傳參的函式
應用場景:偽數轉真陣列、求陣列最大值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// apply背景關系呼叫:修改this指向 傳參不同
// 語法:函式名.apply(修改的this指向,陣列/偽陣列)
function fn(a, b, c) {
console.log(this);
console.log(a * b + c);
}
fn.apply({ name: '陳爽' }, [30, 50, 50])
</script>
</body>
</html>
偽陣列轉真陣列
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let obj = {
0: 20,
1: 30,
2: 40,
3: 90,
4: 100,
length: 5
}
// console.log(obj);
// 將偽陣列轉為真陣列
// ES6語法
// let arr = Array.from(obj)
// console.log(arr);
// ES5語法
// 創建一個空陣列
let Array = []
// 函式名.apply(修改的this,陣列/偽陣列)
// 第一個引數:不用修改this,因為this本來就指向Array
// 第二個引數:obj,借助apply特點:自動遍歷了偽陣列,逐一傳參
Array.push.apply(Array, obj)
console.log(Array);
</script>
</body>
</html>
求陣列最大值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let arr = [20, 40, 90, 10, 32, 90]
// 排序法 // 最大值
// arr.sort(function (a, b) {
// return b - a
// })
// console.log(arr[0]);
// ES5語法
// 第一個引數:Math,因為他this指向本身就Math,這里不用修改this指向,相當于不變
// 第二個引數:arr,借助apply的特點,自動遍歷陣列里的元素,然后逐一傳參,求出最大值賦值給max
let max = Math.max.apply(Math, arr)
console.log(max);
// ES6語法
let max3 = Math.max(...arr)
console.log(max3);
</script>
</body>
</html>
bind()語法:函式名.bind(this修改后的指向,引數1,殘數2)
bind()語法并不會立即執行函式,而是回傳一個修改指向后的新函式,常用于回呼函式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
/*
1.函式三種執行方式 :
全域函式 : this指向window
物件方法: this指向物件
建構式 : this指向new創建的物件
共同的特點: this的指向無法動態修改
2.函式背景關系模式 :
2.1 作用: 可以動態修改函式中的this
2.2 語法: 有3種寫法,作用都是一樣改this,應用場景不同
a. 函式名.call(修改的this,arg1,arg2…………)
* 適用于函式原本形參 <= 1
b. 函式名.apply(修改的this,[陣列或偽陣列])
* 適用于函式原本形參 >= 2
c. 函式名.bind(修改的this,arg1,arg2…………)
* 特點:不會立即執行這個函式,而是回傳修改this后的新函式
* 適用于不會立即執行的函式 : 事件處理函式,定時器函式
*/
// function fn(){
// //三種執行模式this無法動態修改
// //this = {age:18};
// console.log(this);
// };
// fn();//this:window
/* 背景關系模式 */
function fn(a,b){
console.log(this);
console.log(a+b);
};
//a. 函式名.call(修改的this,arg1,arg2…………)
//應用場景: 適用于函式原本形參 <= 1
fn(10,20);//this:window
fn.call({age:18},100,200);
//b. 函式名.apply(修改的this,[陣列或偽陣列])
//應用場景: 適用于函式原本形參 >=2
fn.apply({age:88},[20,30]);
//c. 函式名.bind(修改的this,arg1,arg2…………)
//特點:這個函式不會立即執行,而是回傳一個修改this之后的新函式
//應用場景 : 事件處理函式,定時器
let newFn = fn.bind({name:'坤坤'});
newFn(50,60);
//4. 定時器中的this一定是指向window
// 定時器:一段代碼間隔事件執行 setTimeout(一段代碼,間隔時間)
//4.1 具名函式
let test = function(){
console.log('我是具名函式');
console.log(this);
};
let newTest = test.bind({name:'張三'})
setTimeout(newTest,3000);
//4.2 匿名函式
setTimeout(function(){
console.log('我是定時器中的函式');
console.log(this);
}.bind({name:'李四'}),2000);
</script>
</body>
</html>
//2.3 bind();
//語法: 函式名.bind(this修改后的指向,arg1,arg2....);
let obj = {
name:"文聰兄"
};
function getSum(a,b){
console.log(this);
console.log(a+b);
}
getSum(10,20);//this指向window
let fn = getSum.bind(obj); //bind()不會執行這個函式,而是會回傳一個修改了this的函式.
fn(100,200); //此時這個fn,就相當于是修改了this之后的getSum.
//3 回呼函式(例如定時器),一般使用bind來修改this指向
let obj = {
name:"班長"
};
//3.1 定時器的回呼函式是一個具名函式
function test1(){
console.log(this);
}
let test2 = test1.bind(obj);
setInterval(test2,2000);
//3.2 定時器的回呼函式是一個匿名函式
setInterval(function () {
console.log ( this );
}.bind(obj),2000);
遞回
遞回函式介紹
1.遞回函式:一個函式自己呼叫自己
2.遞回函式特點:
一定要有結束條件,否則會導致死回圈
能夠用遞回函式實作的需求,就一定可以用回圈呼叫函式來解決,只是代碼簡潔與性能不同而已
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
/*
1. 遞回函式 : 在函式中自己呼叫自己
2. 遞回特點
a. 能用遞回實作的功能一定可以用回圈,只是語法不同
b. 遞回一定要有結束的條件,否則會導致死回圈
*/
//一個函式遞回
// function fn(){
// console.log('哈哈');
// fn();
// };
// fn();
//兩個函式遞回
// function fn1(){
// console.log('哈哈');
// fn2();
// };
// function fn2(){
// console.log('呵呵');
// fn1();
// };
// fn2();
//需求:寫一個函式,列印三次 我愛坤哥
let i = 1;
function fn(){
console.log('我愛坤哥');
i++;
if(i <= 3){
fn();
};
//回圈實作
// for(let i = 1;i<=3;i++){
// console.log('我愛坤哥');
// };
};
fn();
</script>
</body>
</html>
遞回應有場景:淺拷貝與深拷貝
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<script>
/*淺拷貝與深拷貝概念主要針對于物件這種資料型別
1.淺拷貝: 拷貝的是地址
* 特點:修改拷貝后的資料,原資料也會隨之修改
2.深拷貝:拷貝的是資料
* 特點:修改拷貝后的資料,對原資料沒有影響
*/
let obj = {
name: 'ikun',
age: 32,
hobby: ['講課', '敲代碼', '黑馬程式員'],
class: {
name: '武漢大前端',
salary: [18888, 12000, 10000]
}
}
//1.淺拷貝: 拷貝的是地址
let obj1 = obj
obj1.name = '黑馬李宗盛'
console.log(obj, obj1)
//2.深拷貝:拷貝的是資料
//核心原理:使用遞回, 只要遇到屬性值是參考型別,則遍歷,
function kaobei (newObj, obj) {
// 遍歷
for (let key in obj) {
if (obj[key] instanceof Array) {
// obj[key] 是陣列
// obj[key]是陣列
newObj[key] = []
kaobei(newObj[key], obj[key])
} else if (obj[key] instanceof Object) {
// obj[key] 是物件
// obj[key]再遍歷拷貝
newObj[key] = {}
kaobei(newObj[key], obj[key])
} else {
newObj[key] = obj[key]
}
}
}
let obj2 = {}
//深拷貝
kaobei(obj2, obj)
//修改拷貝后的資料
obj2.name = '黑馬顏值擔當'
obj2.hobby = '唱歌'
console.log(obj,obj2)
</script>
</body>
</html>
遞回應用場景:遍歷dom樹
-
在我們后期vue專案中,有一個這樣的案例:服務器回傳一個不確定的資料結構, 是一個
多級選單,這個資料是不確定的,我們需要根據服務器回傳的資料,來生成對應的頁面結構
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> <style> * { padding: 0; margin: 0; } .menu p { width: 100px; border: 3px solid; margin: 5px; } .menu > div p { margin-left: 10px; border-color: red; } .menu > div > div p { margin-left: 20px; border-color: green; } .menu > div > div > div p { margin-left: 30px; border-color: yellow; } </style> </head> <body> <div class="menu"> <!-- <div> <p>第一級選單</p> <div> <p>第二級選單</p> <div> <p>第三級選單</p> </div> </div> </div> --> </div> <script> //服務器回傳一個不確定的資料結構,涉及到多重陣列嵌套 let arr = [ { type: '電子產品', data: [ { type: '手機', data: ['iPhone手機', '小米手機', '華為手機'] }, { type: '平板', data: ['iPad', '平板小米', '平板華為'] }, { type: '智能手表', data: [] } ] }, { type: '生活家居', data: [ { type: '沙發', data: ['真皮沙發', '布沙發'] }, { type: '椅子', data: ['餐椅', '電腦椅', '辦公椅', '休閑椅'] }, { type: '桌子', data: ['辦公桌'] } ] }, { type: '零食', data: [ { type: '水果', data: [] }, { type: '咖啡', data: ['雀巢咖啡'] } ] } ] /* 使用遞回遍歷陣列 */ function addElement (arr, father) { for (let i = 0; i < arr.length; i++) { let div = document.createElement('div') div.innerHTML = `<p>${arr[i].type || arr[i] }</p>` father.appendChild(div) if( arr[i].data ){ addElement(arr[i].data,div) } } } //呼叫遞回函式 addElement(arr,document.querySelector('.menu')) </script> </body> </html>閉包
1.閉包:是一個可以訪問其他函式變數的函式
2.閉包作用:解決變數污染問題,讓變數被函式保護起來
function fn(){
let a = 1
function fn1() {
console.log(a)
}
fn1()
}
執行函式 fn1 用到了另一個函式fn中的 a 這個變數,所以 fn1 + a 構成了閉包,
閉包的案例
-
案例需求:在輸入框輸入搜索文字,點擊百度一下按鈕,用定時器模擬網路請求,1 秒之后顯示搜索結果;
結構如下:
<div class="box">
<input type="search" name="" id="">
<button>百度一下</button>
</div>
代碼如下:
// 1. 獲取元素
let search = document.querySelector('.box input')
let btn = document.querySelector('.box button')
// 2. 添加點擊事件
btn.onclick = function () {
// 獲取搜索的文字
let text = search.value
// 模擬發送網路請求
setTimeout(function () {
alert(`您搜索的內容是 ${text} 共搜索到 12345 條結果`)
}, 1000)
}
學習重點梳理(高頻面試題)
this三種指向
this:誰呼叫我,我就指向誰
1.全域函式:this指向window
2.物件方法:this指向物件
3.建構式:this指向new創建的空物件
call、apply、bind三者區別
相同點:都是修改函式this指向
不同點:
傳參方式不同:call用于單個引數,apply用于多個引數(陣列/偽陣列)
執行機制不同:call與apply會立即執行,bind不會立即執行
call、apply用一次修改一次
bind:依次修改,終生有效
閉包
什么是閉包:以下兩種回答都可以
閉包是一個訪問其他函式內部變數的函式
閉包是函式+背景關系代碼組合
閉包作用:解決變數污染
遞回
什么是遞回:函式內部呼叫自己
遞回場景
深拷貝
遍歷dom樹
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/395140.html
標籤:其他
上一篇:JS--閉包--作用/用途/原理
