JS高级知识点总结

本文详细介绍了JavaScript的高级特性,包括闭包的应用、作用域链、垃圾回收机制、let、const与var的区别、函数参数、箭头函数、多维数组与多级对象解构、创建对象的三种方式、静态成员与实例成员的区别、数组的高阶方法、this的指向场景、浅拷贝、递归函数、深拷贝、异常处理、对象获取属性名和值的方式、节流与防抖等知识点,适合进阶学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.闭包以及应用场景:


闭包=内层函数+外层函数的变量

表现形式:函数之间相互嵌套

内层函数能够访问到外层函数的变量

作用:延长变量的生命周期,能够使函数外部访问到函数内部的变量(实现数据私有)

缺点:会使内存泄漏

1.1通过闭包的形式统计执行次数


functionfn(){

leti=0

functioninner(){

i++

console.log(`函数执行调用了${i}次`)

}

retureninner

}

constresult=fn()

result()//函数执行调用了1次

result()//函数执行调用了2次

result()//函数执行调用了3次

2.作用域链


1)作用域链本质是底层的变量查找机制

2)函数执行是优先在当前作用域查找变量,如果没有会逐层向上级作用域查找,直到全局作用域

3.垃圾回收机制


垃圾回收机制简称GC

1)在JS中内存的分配和回收都是自动完成的,内存在不使用时会被垃圾回收器自动回收

2)不了解垃圾回收机制很容易造成内存泄漏(就是用不到的内存,没有及时释放会造成内存泄漏)

3.1内存的生命周期


1)内存分配:声明变量、函数、对象时系统会自动给他们分配内存

2)内存使用:操作这些变量、函数、对象就是使用内存

3)内存回收:使用完毕后,垃圾回收器自动回收不再使用的内存

注意:

全局变量一般不会被回收,只有关闭页面的时候才会被回收

局部变量不使用就会被自动回收

3.2常见的浏览器垃圾回收算法(引用计数法、标记清除法)


3.2.1引用计数法

看一个对象是否有指向它的引用

算法:

1)跟踪记录每个值被引用的次数

2)这个值被引用一次,就会记录次数1

3)多次引用会累加

4)减少引用会使记录次数减1

5)引用次数为0时,释放内存

3.2.2标记清除法

核心:

1)标记清除法将‘不再使用的对象’定义为‘无法到达的对象’

2)就是从根部(JS的全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象都是还需要使用的

3)那些无法从根部到达的对象标记为不在使用,会被回收器自动回收

4.let,const和var的区别


1)let声明的变量会产生块级作用域,var不会

2)let声明的变量不挂载再window对象上,var声明的变量时挂载再window对象上的

3)let声明的变量不存在变量声明的提升,var声明的变量存在,但是只提升声明,不提升赋值

4)const声明常量,不可以更改赋值

5)实际开发中推荐先声明在访问变量

5.函数的参数


5.1动态参数(arguments)


arguments是函数内部内置的伪数组变量,它包含调用时传入的所有实参

// 需求:定义一个函数,求任意数的和

function getSum(){

console.log(arguments)

let sum = 0

for(let i=0; i<arguments.length; i++){

sum+=arguments[i]

}

console.log(sum)

}

getSum(1,2,3,6,4,8,6)//30

getSum(2,36,7,4,1)//50

//1)arguments是一个伪数组,只存在于函数中

//2)arguments的作用是动态获取函数的实参

//3)可以通过for循环遍历到传递过来的实参

5.2剩余参数


语法:function 函数名(...变量名){}

注意:

1)在函数内部使用的时候直接使用变量名

2)剩余参数的位置必须在函数所有参数的最后一个位置

值:是一个真数组,可以调用数组的方法包含的是函数调用时候剩余的实参

// 需求:定义函数,求任意数的和

function getSum(...arrs){

console.log(arrs)

let sum = 0

for(let i = 0; i<arrs.length; i++){

sum+=arrs[i]

}

console.log(sum)

}

getSum(1,2,3,4,5,6,7,8,9)//45

--------------------------------------------------------------

function fn(a,b,...arrs){

console.log(arrs);

}

fn(5,6)//[]

fn(1,2,3,5,4)//[3,5,4]

fn(12,54,26,98)//[26,98]

5.3展开运算符(数组的展开运算符)


语法:...数组名

作用:展开数组

经典应用场景:

1)合并数组

2)求数组的最大值和最小值(使用Math.max()方法、使用Math.min()方法)

3)对象的拷贝(浅拷贝)

//展开数组

let arr = [10,2,3,4]

console.log(...arr);//10 2 3 4

-----------------------------------------------

//合并数组

let arr = [10,2,3,4]

let arr2 = [33,66,45,99]

// 第一种方法

let arr3 = [...arr,...arr2]

console.log(arr3);//[10, 2, 3, 4, 33, 66, 45, 99]

//第二种方法

console.log(arr.concat(arr2))//[10, 2, 3, 4, 33, 66, 45, 99]

-------------------------------------------------------

// 求最大值和最小值

let max = Math.max(...arr,...arr2)

let min = Math.min(...arr,...arr2)

console.log(max,min);//99 2

-------------------------------------------------------

// 使用展开运算符进行对象的拷贝

let obj = {name:123,age:12,loc:12,}

let o = {...obj}

console.log(obj);//{name: 123, age: 12, loc: 12}

console.log(o);//{name: 123, age: 12, loc: 12}

6.箭头函数


箭头函数的基本语法:基本语法:let xxx= ()=>{}

注意:箭头函数里面有没有arguments

this指向window

let fn = () =>{

consloe.log(11)

}

----------------------------------------------------

//改造箭头函数

let fn = (a,b) =>{

console.log(a+b)//3

}

fn(1,2)

-------------------------------------------------------

//简写1:如果箭头函数的参数只有一个,那么()可以省略

let fn= a =>{

console.log(a)

}

fn(123)

---------------------------------------------------------

//简写2:如果箭头函数的参数只有一行代码,那么{}可以省略

let fn = a => console.log(a)

fn(123)

---------------------------------------------------------

//简写3:如果箭头函数的参数只有一行代码,并且使用return返回函数的结果,那么{}和return都可以省略

let sum = (a,b) => a+b

const aa = sum(1,2)

console,log(aa)

------------------------------------------------------------

//简写4:如果箭头函数的参数只有一个对象,并且return返回的是一个对象,那么{}和return可以省略,但对象需要加上()

let fn = () =>({name:'aa',age:18})

console.log(fn)

7.多维数组和多级对象如何解构


7.1多维数组解构:


语法:let(var、const)[变量名1,变量名2,...]=[元素1,元素2,...]

作用:将数组中的元素快速批量赋值给一系列的变量的简写操作

结果:是将数组的元素按照位置一一对应赋值给左边的变量

//经典应用场景 交换两个数

let a = 10

let b = 20

[b,a] = [a,b]

console.log(a,b)//20 10

-----------------------------------------------------------

//变量名多,数组元素少会出现undefined的情况

let [a,b,c,d] = [1,2,3]

console.log(a,b,c,d)//1 2 3 undefined

------------------------------------------------------------

//避免出现undefined的情况可以设置默认值

let [a,b,c,d=0] = [1,2,3]

console.log(a,b,c,d)//1 2 3 0

------------------------------------------------------------

//变量名少,数组元素多会剩余元素,使用剩余参数

let [a,b,c,...arrs] = [1,2,3,4,5,6,7,8,9]

console.log(a,b,c,arrs)//1 2 3 [4,5,6,7,8,9]

---------------------------------------------------------------

//按需导入,忽略某些返回值

let arr = [1,2,3,4,5,6]

let [a,,b,c] = arr

console.log(a,b,c)//1 3 4

7.2多级对象结构:


语法: 第一种情况: 对象解构变量名要和属性名保持一致

let {属性名,属性名}={属性名:属性值,属性名:属性值,...}

结果:是按照属性名进行解构所以变量名可以互换位置

第二种情况: 对象解构变量名和属性名不一致的情况

let {属性名:别名,属性名1:别名1,...}={属性名:属性值,属性名1:属性值,...}

结果:是按照属性名进行解构,所以变量名可以互换位置

let obj = {

name: "zs",

age: 18,

};

//第一种情况: 对象解构变量名要和属性名保持一致

let { name, age } = obj;

console.log(name, age);

----------------------------------------------------------------

// 第二种情况: 对象解构变量名和属性名不一致的情况

let { name: uname, age: age1 } = obj;

console.log(uname, age1);

//注意:以前的名不可以使用

思考题

//1)如何把3,和5解构出来

let arr = [1,2,[3,4,[5,6]]]

let [a,b,[c,d,[e,f]]]=arr

console.log(c,e)

-------------------------------------------------------------

//2) 如何将华为和小米解构出来

let shop = [{id:1,name:'小米'},{id:2,name:'华为'}]

shop.forEach(function(item){

// console.log(item);

let {id,name} = item

console.log(item.name);//小米 华为

})

--------------------------------------------------------------

//3)将猪妈妈和猪爸爸解构出来

let pig={ name:'佩奇',age:6,family:{mother:'猪妈妈',father:'猪爸爸'}}

let {name,age,family:{mother,father}} = pig

console.log(mother,father);//猪妈妈 猪爸爸

8.创建对象三种方式


8.1利用对象字面量创建对象


const o = {

name:'佩奇'

}

8.2利用new Object 创建对象


const o = new Object({

name:'佩奇'

})

8.3利用构造函数创建对象


[1]定义构造函数:function 函数名(){this.属性名=属性值}

[2]创建对象:let xxx= new 函数名()

约定:

1)构造函数的函数名首字母要大写

2)创建对象使用new关键字

注意:

使用new关键字创建的对象是结构相同,但不是同一个对象

在构造函数内部无需写return,即使写了也无效,因为构造函数内部会自动返回新创建的对象

在构造函数中this指向的是实例对象

8.3.1 new关键字干的事情:

new关键字干的事情:

【1】创建一个新的空对象

【2】将this指向这个新的空对象

【3】执行构造函数中的代码,给this添加属性和方法

【4】返回新对象

注意:使用new关键字创建对象也成为对象实例化的过程,创建的对象称为“实例对象”,简称为“实例”

//利用构造函数创建对象

function Pig(name,age){

this.name = name

this.age = age

}

// 使用new关键字创建对象也成为对象实例化的过程,创建的对象称为“实例对象”,简称为“实例”

const p = new Pig('佩奇',18)

console.log(p)

9.静态成员和实例成员的区别


注意:万物皆对象,函数既是函数,也是对象

9.1静态成员


给构造函数上面的的属性和方法称为静态成员,只能由构造函数访问,实例对象不可以访问

9.2实例成员


实例对象上面的属性和方法称为实例成员,只能由实例对象访问,构造函数不可以访问

function Pig(name,age){

// 这里的this指向的是实例对象

this.name= name

this.age=age

}

let pq = new Pig('佩奇',9)

console.log(pq);

console.log(pq.name)

Pig.eat = function(){

// 这里的this指向的是构造函数本身

console.log(this);

console.log('吃饭');

}

Pig.eat()

Pig.head = 4

console.log(Pig.head);

console.log(Pig);

10.数组的5个高阶方法(方法,作用,返回值)


10.1reduce():数组累加计算


语法:数组.reduce(function(累计值,每一项元素,[索引(index)],[原数组]){},[起始值])

参数:

【1】如果有起始值,累加值=起始值

【2】如果没有起始值,累加值是数组的第一个元素进行累计

【3】每次循环遍历数组的元素会累计到累计值里面

返回值:返回的是所求的累加结果

let arr = [1,2,3,4,5]

let result = arr.reduce(function(prev,curr){

return prev+curr

},0)

console.log(result)//15

10.2map():遍历数组


语法:数组.map(function(item,index){})

item:数组元素 index:数组索引号,下标

作用:遍历数组,对数组的每一项进行操作

返回值:返回新数组,不会影响原数组

let arr = [1, 2, 3, 4];

const map1 = arr.map(function(item){

return item+2

})

console.log(map1)//[3,4,5,6]

10.3filter():过滤


语法:数组名.filter()

作用:过滤返回满足某个条件的数组中的元素

返回值:返回新数组,不影响原数组

const arr = [1, 20, 3, 60, 5, 4, 1, 23, 5, 10];

const newArr = arr.filter(function (item) {

return item > 10;

});

console.log(newArr);// [20, 60, 23]

10.4every():(一假则假,全真为真)


语法:数组名.every(function(item,[index]){return 条件})

作用: 判断数组中所有的元素是否都符合条件

返回值: 返回值是一个布尔值,不影响原数组

如果数组中所有的元素都符合条件,那么结果是true,反之,只要有一个不符合条件,结果就是false

const arr = [1, 5, 6, 8, 7, 4, 5, 6];

const newArr = arr.every(function (item, index) {

return item > 0;

});

console.log(newArr);//true

10.5some():(一真则真,全假为假)


语法:数组名.every(function(item,[index]){return 条件})

作用:判断数组中是否符合条件的元素

返回值: 返回值是一个布尔值,不改变原数组

如果数组中有符合条件的元素,返回的是true

如果数组中所有的元素都不符合条件,返回的是false

const arr = [1, 5, 6, 8, 7, 4, 5, 6];

const newArr = arr.some(function (item, index) {

return item > 6;

});

console.log(newArr);//true

10.6find():查找数组中符合条件的第一个元素


语法:数组.find(function(item,[index]{returnt 条件})

作用:查找数组中符合条件的第一个元素

返回值:有返回值,如果有符合条件的元素,那么会返回第一个符合条件的数组元素,如果没有,返回的是undefined,不会影响原数组

const arr = [1, 5, 6, 8, 7, 4, 5, 6];

const newArr = arr.find(function (item) {

return item === 5;

});

console.log(newArr);//5

10.7forEach():遍历数组


语法:数组名.forEach(function(item,index){})

作用:遍历数组

返回值:没有返回值

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

arr.forEach(function (item, index) {

console.log(`该元素为:${item},数组下标为:${index}`);

});

10.8indexOf():返回找到元素的索引号


语法:数组.indexOf(查找的元素)

作用:返回查找元素的索引号,如果不存在返回-1

返回值:查找元素的索引号,如果不存在返回-1

const arr = [1,2,3,4,5,6]

let aa = arr.indexOf(5)

console.log(aa);//4

10.9join拼接字符串


语法:数组名.join('以啥样子的形式拼接')

作用:拼接字符串

返回值:返回拼接好的字符串

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

let newArr = arr.join("-");

console.log(newArr);//1-2-3-4-5-6-7-8-9-10

console.log(arr);//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

10.10reverse 颠倒数组,第一个变成最后一个,最后一个变成第一个


语法:数组名.reverse()

作用:反转数组,会改变原数组

返回值:返回新数组

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

let newArr = arr.reverse();

console.log(newArr); //[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

console.log(arr);// [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

10.11sort 对数组进行排序


语法:数组名.sort()

作用:对数组进行从小到大排序,会改变原数组

返回值:返回新数组

const arr = [12, 21, 30, 14, 53, 16, 27, 38, 89, 10];

let newArr = arr.sort();

console.log(newArr);//[10, 12, 14, 16, 21, 27, 30, 38, 53, 89]

console.log(arr);//[10, 12, 14, 16, 21, 27, 30, 38, 53, 89]

10.12concat 合并数组


语法:数组名1.concat(数组名2)

作用:对数组进行拼接,不会改变原数组

返回值:返回新数组

const arr1 = [1, 2, 3, 6, 5];

const arr2 = [9, 8, 5, 2, 1, 1];

const newArr = arr1.concat(arr2);

console.log(newArr);//[1, 2, 3, 6, 5, 9, 8, 5, 2, 1, 1]

console.log(arr1);//[1, 2, 3, 6, 5]

console.log(arr2);//[9, 8, 5, 2, 1, 1]

10.13from将伪数组转为真数组


语法:Array.from(伪数组)

作用:将伪数组转为真数组

返回值:返回值是一个真数组

const lis = document.querySelectorAll("ul li");

const newArr = Array.from(lis);

console.log(newArr);

10.14slice:截取数组元素


语法:数组名.slice(start(数组下标),end(数组下标))

作用:截取从start位置到end-1位置处的数据

返回值:返回截取后的数组

const arr = [1, 2, 3, 5, 4, 6, 8, 4, 2];

const newArr = arr.slice(2, 7);

console.log(newArr);// [3, 5, 4, 6, 8]

11字符串的5个方法(方法作用,返回值)


11.1trim()删除字符串两端的空白字符


语法:字符串.trim()

作用:删除字符串两端的空白字符

返回值:一个去掉两端空白字符的新字符串

const str = ' Hello world! ';

console.log(str);//" Hello world! "

console.log(str.trim());//"Hello world!"

11.2 replace()替换字符串


语法:字符串.replace('查找要替换的内容','要替换的新内容')

作用:替换字符串的内容,不会改变原字符串

返回值:返回替换后的新字符串

const str = '今天又要做核酸了'

const newStr = str.replace('又要','不要')

console.log(newStr);//今天不要做核酸了

console.log(str);//今天又要做核酸了

11.3split():将字符串转成数组


语法:字符串.split('分隔符')

作用:将字符串转成数组

返回值:是一个数组,并且不会影响原字符串

const arr = "2022-01-01";

const newArr = arr.split("-");

console.log(newArr);// ['2022', '01', '01']

console.log(arr);//2022-01-01

11.4toUpperCase():字符串转大写


语法:字符串.toUpperCase()

作用:字符串转大写

返回值:是一个新的字符串,不会影响原字符串

const str = 'aaa bbb'

const newStr = str.toUpperCase()

console.log(str);//aaa bbb

console.log(newStr);//AAA BBB

11.5toLowerCase():字符串转小写


语法:字符串.toLowerCase()

作用:字符串转小写

返回值:是一个新的字符串,不会影响原字符串

const str = 'AAA BBB'

const newStr = str.toLowerCase()

console.log(newStr);//aaa bbb

console.log(str);//AAA BBB

11.6substring():字符串的截取


语法:字符串.substring(开始的索引号,[结束的索引号])

作用:字符串截取

返回值:返回截取的部分

const str = "今天又要做核酸了";

console.log(str.substring(5, 7));//核酸

11.7 substr():字符串的截取(尽量使用substring())


语法:字符串.substr(开始的索引号,[截取的长度])

作用:截取字符串

返回值:截取的部分

注意:如果 截取的长度 为 0 或负值,则 substr 返回一个空字符串。如果忽略 截取的长度,则 substr 提取字符,直到字符串末尾。

const str = 'abcdefgfijk'

const newStr = str.substr(2,4)

console.log(newStr);//cdef

console.log(str);//abcdefgfijk

11.8indexOf()返回查找字符串的索引号


语法:字符串.indexOf(查找的元素)

作用:返回查找元素的索引号,如果不存在返回-1

返回值:查找元素的索引号,如果不存在返回-1

const str = '今天又要做核算了'

const newStr = str.indexOf('又')

console.log(newStr);//2

console.log(str);

11.9includes():判断字符串中是否有要找的字符串


语法:字符串.includes('要搜索的字符串',[检测位置的索引号])

作用:判断某个字符串是否是包含'要搜索的字符串'

返回值:返回的是一个布尔值,如果包含结果是true,否则是false

注意点: 对大小写敏感

const str = '今天又要做核算了'

const newStr = str.includes('又')

console.log(newStr);//true

console.log(str);

11.10startsWith():判断字符串是不是以要检测的字符串开头


语法:字符串.startWith('要检测的字符/字符串',[检测位置索引号])

作用:判断某个字符串是以'要检测的字符/字符串'开头

返回值:结果是一个布尔值,如果是以某个字符/字符串开头,则返回的是true,否则是false

注意点:大小写敏感

const str = '今天又要做核算了'

const newStr = str.startsWith('今')

console.log(newStr);//true

console.log(str);

11.11endsWith():判断字符串是不是以要检测的字符串结尾


语法:字符串.endsWith('要检测的字符/字符串',[检测位置索引号])

作用:判断某个字符串是以'要检测的字符/字符串'结尾

返回值:结果是一个布尔值,如果是以某个字符/字符串结尾,则返回的是true,否则是false

注意点:大小写敏感

const str = '今天又要做核算了'

const newStr = str.endsWith('今')

console.log(newStr);//false

console.log(str);

12.什么是原型


面试回答:

什么是原型链?

原型链能干吗?

为对象和属性查找指明方向

具体查找机制是怎么样

原型对象:js规定,每一个构造函数都有一个属性prototype,指向一个新对象,这个对象称为原型对象,简称原型

作用:可以将一些不变的(公共的)方法挂载在原型对象上面,所有的实例对象共享这些方法

好处:节省内存

this指向:构造函数和原型对象中this都指向实例对象

function Star(name, age) {

this.name = name;

this.age = age;

/* this.sing = function(){

console.log('唱歌');

} */

}

// 将一些不变的(公共的)方法,挂载在原型对象上面

Star.prototype.sing = function(){

console.log('会唱歌');

}

const ldh = new Star('刘德华',18)

ldh.sing()

const zxy = new Star('张学友',50)

zxy.sing()

console.log(ldh.sing === zxy.sing);//true

12.1补充求最大值和最小值的方法


Array.prototype.max = function(){

return Math.max(...this)//this都指向实例对象

}

Array.prototype.min = function(){

return Math.min(...this)//this都指向实例对象

}

const arr = [1,2,3,6,9,88]

console.log(arr.max());//88

console.log(arr.min());//1

12.2给数组扩展求和的方法


Array.prototype.sum = function(){

return this.reduce((prev.curr) => prev+curr,0)

}

const arr = [1,2,3,4,5]

console.log(arr.sum())//15

12.3constructor属性


每个原型对象上面都有一个属性constructor,指向构造函数本身

作用:能够知道原型对象是由那个构造函数产生

// 如果给原型对象直接赋值为一个对象的形式挂载方法,会丢失constructor属性,不再指向之前的构造函数

// 需要手动添加一个constructor,指向构造函数

function Star(name, age) {

this.name = name;

this.age = age;

}

// Star.prototype.sing = function(){console.log('唱歌');}

Star.prototype = {

constructor: Star,

sing: function () {

console.log("唱歌");

},

dance: function () {

console.log("跳舞");

},

movie: function () {

console.log("演电影");

},

};

const lah = new Star("刘德华", 60);

console.log(Star.prototype);

console.log(Star.prototype.constructor);

console.log(Star.prototype.constructor === Star);

12.4对象原型:__proto__


每个对象都有一个属性__proto__,称为对象原型,指向构造函数的原型对象

注意:

1)__proto__是一个js非标准的属性,只能读取,不能修改

2)[[Prototype]]和__proto__的意义是一样的

function Star(name, age) {

this.name = name;

this.age = age;

}

Star.prototype.sing = function(){

console.log('会唱歌');

}

const ldh = new Star('刘德华',18)

console.log(ldh.__proto__);

const ldhh = ldh.__proto__

ldhh.sing()//会唱歌

console.log(ldh.__proto__.constructor);//指向的是Star构造函数

console.log(ldh.__proto__.constructor === Star);//true

12.5原型链


原型对象也是对象,也拥有__proto__属性,指向上一级原型对象,这种层层向上的关系,像链条一样,称为原型链

注意:原型链也有终点,原型链的终点是Object.prototype.__proto__ === null

作用:为对象查找属性和方法指明一条道路

查找机制:

对象访问属性和方法的时候,先在对象自身上面查找,自身没有,就顺着原型链向上查找,直到找到原型链的终点,如果对象没有某个属性,结果是undefined;如果没有某个方法,结果会报错

function Star(name, age) {

this.name = name;

this.age = age;

}

Star.prototype.sing = function(){

console.log('会唱歌');

}

const ldh = new Star('刘德华',18)

/* console.log('原型对象',Star.prototype);

console.log('上一级原型对象',Star.prototype.__proto__);

console.log('上一级的上一级的原型对象',Star.prototype.__proto__.__proto__);//null */

console.log('原型对象',Star.prototype);

console.log('上一级原型对象',Star.prototype.__proto__ === Object.prototype);//true

console.log('上上级原对象',Object.prototype.__proto__);//null

12.6原型继承


子构造函数的原型对象,指向父构造函数的实例对象

子构造函数的原型对象 = new 父构造函数

function Person(){

this.head = 1

this.eyes = 2

}

// 女人

function Woman(){}

// 女人要继承Person上面的属性

Woman.prototype = new Person()

Woman.prototype.constructor = Woman

// console.log(Woman);

// 生孩子

Woman.prototype.baby = function(){

console.log('生孩子');

}

const w = new Woman()

console.log(w);//woman构造函数

w.baby()//生孩子

console.log(w.head);//1

console.log(w.eyes);//2

// 男人

function Man(){}

Man.prototype = new Person()

Man.prototype.constructor = Man

const m = new Person()

console.log(m);

console.log(m.head);

m.baby()//找不到,会报错

12.7instanceof运算符(检测的对象是否在某个构造函数上)


语法:检测的对象 instanceof 某个构造函数

作用:用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

返回值: 是一个布尔值,如果构造函数在检测对象的原型链上,结果就是ture,否则就是false

function Star(){

this.name = 'zs';

this.age = 18;

}

let arr = [1,2,3,4]

console.log(arr instanceof Array)//true

console.log(arr instanceof Object)//true

console.log(arr instanceof Star)//false

12.8如何判断一个变量是否是数组


//如何判断一个变量是不是数组

let arr1 = [1, 2, 3, 5, 4, 8];

let obj = { name: "zx", age: 18 };

// 第一种方法

console.log(arr instanceof Array);//true

console.log(obj instanceof Array);//false

// 第二中方式

console.log(Array.isArray(arr));//true

console.log(Array.isArray(obj));//false

13.说一下this的指向场景


13.1普通情况下:


①普通函数中this指向window

②在定时器中this指向的是window

③在立即执行函数中this指向的是window

④事件处理函数中this指向的是事件源

⑤在构造函数和原型对象中this指向的是实例对象

⑥在对象的方法中的this是谁调用指向谁

⑦箭头函数中没有自己的this,沿用上一级this,直到找到全局

13.2严格情况下:


开启严格模式(’use strict‘)

在严格模式下,变量必须先声明在使用否则会报错

在严格模式下,普通函数的指向为undefined

13.3改变this的指向:(call和apply和bind)


1)call:

语法:函数名.call(更改的this的值,实参1,实参2,实参3,...)

作用:【1】调用函数

【2】更改函数内部的this指向

返回值:返回值取决于函数的返回值

2) apply:

语法:函数名.apply(更改的this值,[实参1,实参2,...])

作用:

【1】调用函数

【2】更改函数内部的this指向

返回值: 取决于函数的返回值

call()和apply()的区别:传入的实参形式不同

3)bind:

语法:函数名.bind(更改的this,实参1,实参2,...)

作用:更改函数内部的this指向

返回值:返回值是一个函数,是原函数的拷贝,是一个新函数

注意点:bind不能调用函数

应用场景:如果仅仅想要改变this指向,不调用函数可以使用bind()

三者相同点:都可以改变函数内部的this指向

三者区别:

call和apply会调用函数,并且改变函数内部this指向

call和apply传递的参数不一样,call传递的参数arr1,arr2,...形式 apply必须数组形式[arr1,arr2,...]

bind不会调用函数,可以改变函数内部this的指向

call和apply的返回值取决于函数的返回值,bind的返回值是一个新的函数

主要应用场景:

call调用函数并且可以传递参数

apply经常跟数组有关系,比如借助数学对象实现数组最大值最小值

bind不调用函数,但是还想改变this的指向,比如改变定时器内部的this指向

14.浅拷贝


如果对象的属性值是简单数据类型,拷贝的是值

如果对象的属性值是复杂数据类型,拷贝的是地址

注意:浅拷贝拷贝的是单层对象,如果是多层对象就会有问题

const obj = {

name: "zs",

obj: 18,

hobbies: ["吃饭", "睡觉", "打豆豆"],

};

// 第一种方式 ...(展开运算符)

(function () {

const o = { ...obj };

o.name = "李四"; //不会改变原对象的内容

o.hobbies[0] = "吃零食"; //因为它拷贝的是地址,所以会改变原内容

console.log(o);

console.log(obj);

})();

// 第二种方式 Object.assign(目标对象,源对象)

(function(){

const o = {}

Object.assign(o,obj)

o.name = '王五'

o.hobbies[2] = '敲代码'

console.log(o);

console.log(obj);

})();

// 第三种方式 for ... in

(function(){

const o ={}

for(let key in obj){

// 键:key 值:obj[key]

o[key]=obj[key]

}

o.name = 'scsc'

o.hobbies[0] = '打篮球'

console.log(o);

console.log(obj);

})();

15.递归函数


函数在内部自己调用自己,就称为递归函数

注意:递归一定要有终止的条件return,否则会产生死递归,结果会报错,报错栈溢出

// 需求:使用递归实现,让函数里面的代码执行6次

(function () {

let i = 0;

function fn() {

i++;

console.log(`第${i}次`);

if (i >= 6) return;

fn();//自己调用自己

}

fn();

})();

16.深拷贝


如果对象的属性值是一个数组,拷贝的是一个全新的数组

如果对象的属性值是一个对象,拷贝的是一个全新的对象

const obj = {

name: "zx",

age: 18,

hobbies: ["吃饭", "睡觉", "打豆豆",{address:'山西省'}],

family: {

baby: "佩奇",

},

};

// 实现深拷贝

// 注意:Array和Object不可以互换位置,因为万物皆对象(数组也是对象,直接进入Object里,不会执行下边的代码)

let o ={}

for(let key in obj){

// 键:key 值:obj[key]

let item = obj[key]

// 判断属性值是否是一个数组

if(item instanceof Array){

// 证明是一个数组

// 开辟一个空数组

o[key] = []

// 循环遍历item,将item的每一项添加到空数组中

for(let k in item){

// 索引为:k 元素为item[k]

o[key][k] = item[k]

}

}else if(item instanceof Object){

// 判断属性值是不是一个对象

// 证明是一个对象

// 开辟一个空对象

o[key] = {}

// 循环遍历item,将item的每一项添加到空对象中

for(let k in item){

o[key][k] = item[k]

}

}else{

// 属性值是一个简单数据类型

o[key] = item

}

}

o.hobbies[0] = '敲代码'

o.family.baby = '猪妈妈'

console.log(o);

console.log(obj);

16.1递归实现深拷贝


const obj = {

name: "zx",

age: 18,

hobbies: ["吃饭", "睡觉", "打豆豆", { address: "山西省" }],

family: {

baby: "佩奇",

},

gender: "男",

};

let o = {};

// 递归实现深拷贝

function deepCopy(newObj,oldObj){

for(let key in oldObj){

const item = oldObj[key]

if(item instanceof Array){

// 证明属性值item是一个数组

newObj[key] = []

// 将item中额属性值取出来添加到空数组中

deepCopy(newObj[key],item)

}else if(item instanceof Object){

// 证明属性值item是一个对象

newObj[key] = {}

// 将item中的属性名和属性值取出来添加到空对象中

deepCopy(newObj[key],item)

}else{

// 证明属性值item是一个简单数据类型

newObj[key] = item

}

}

}

deepCopy(o, obj);

o.hobbies[3].address='山东省'

console.log(o);

console.log(obj);

16.2使用lodash实现深拷贝


引入lodash.min.js调用_.cloneDeep()实现深拷贝

<script src="./lodash.min.js"></script>

<script>

const obj = {

name: "zx",

age: 18,

hobbies: ["吃饭", "睡觉", "打豆豆", { address: "山西省" }],

family: {

baby: "佩奇",

},

gender: "男",

};

// 使用loadsh的cloneDeep实现深拷贝

const o = _.cloneDeep(obj)

o.hobbies[3].address = '北京'

console.log(o);

console.log(obj);

16.3利用JSON实现深拷贝


//使用JSON.stringify(obj)转成字符串再转对象

const obj = {

name: "zx",

age: 18,

hobbies: ["吃饭", "睡觉", "打豆豆", { address: "山西省" }],

family: {

baby: "佩奇",

},

gender: "男",

};

const o = JSON.parse(JSON.stringify(obj));//JSON.parse转为对象

o.hobbies[3].address = '北京'

console.log(o);

console.log(obj);

17.JS的异常处理


17.1throw抛出异常


throw抛出异常,后面的程序会中断执行

语法:throw new Error('错误信息提示')

let str = 'abcdef'

// 需求:定义函数,实现将字符串转大写

function fun(str){

if(!str){

// throw抛出异常,后面的程序会中断执行

// throw后面跟的是错误信息

// throw一般要搭配Error使用

throw new Error('您没有传参,请把参数传递过来')

}

console.log(str.toUpperCase());

}

fun()

17.2try-catch捕获异常(错误代码会被拦截,不会中断程序)


try{

// 放容易出错的代码

const p = document.querySelector('.p')

p.style.color = 'red'

}catch(err){

// 如果try里面的代码会报错,会被catch拦截,但是代码不会中断程序继续执行

console.log(err);

console.dir(err);

console.log(err.message);

}finally{

// 无论程序是否会发生错误,finally里面的代码都会执行

console.log(22);

}

console.log(11);

17.3debugger断点调试


const arr = [1, 2, 3, 4, 5, 6];

arr.forEach((item, index) => {

debugger;

console.log(`每一项数组元素${item}`);

debugger;

console.log(`数组的下标${index}`);

});

18.对象获取属性名和值的方式都有哪些方式


//Object.values(obj);获取对象的属性值

//Object.keys(obj)获取对象的属性名

let obj = {

name: "aa",

age: 18,

};

const o = Object.values(obj);

console.log(o);//['aa', 18]

const b = Object.keys(obj)

console.log(b);//['name', 'age']

19.节流防抖


19.1节流


节流:使用闭包的形式

核心思路:时间相减

定义: 时间连续触发,但是在n秒内只执行一次

应用场景:轮播图,屏幕缩放resize,鼠标移动事件,滚动条滚动等

// 需求:鼠标移动到盒子上,让数字+1

// 获取元素

const box = document.querySelector(".box");

let i = 0;

// 事件处理函数

function move(e) {

// 数字加1

box.innerHTML = ++i;

console.log(this);//要求这里的this指向事件源

console.log(e);

}

// 定义节流函数

function throttle(fn, delay) {

// 定义起始时间

let startTime = 0;

return function (...args) {

// console.log(e);

console.log(this);//指向事件源

// 获取当前时间

const currentTime = Date.now();

// 时间相减

if (currentTime - startTime >= delay) {

// 执行函数

// fn();

// fn.call(this,e)

fn.apply(this,args)

// console.log(this);

// 修改起始时间

startTime = currentTime;

}

};

}

box.addEventListener("mousemove", throttle(move, 500));

19.2防抖


防抖:

定义:事件触发之后n秒执行一次,如果在n秒内连续触发事件,则要重新计时

核心思路:使用定时器清除和开启

注意:防抖在n秒内多次触发事件,只执行最后一次事件

// 需求:鼠标在盒子移动+1

const box = document.querySelector(".box");

let i = 0;

function move(e) {

box.innerHTML = ++i;

console.log(this);

console.log(e);

}

// 定义防抖函数

function debounce(fn, delay) {

// 定义定时器

let timerId = null

return function(...args){

// console.log(this);

//判断是否开启了定时器

if(timerId) clearTimeout(timerId)

// 开启定时器

timerId = setTimeout(()=>{

fn.apply(this,args)

},delay)

/* timerId = setTimeout(function(){

fn()

},delay) */

}

}

box.addEventListener("mousemove", debounce(move,500));

19.3使用lodash节流防抖


const box = document.querySelector(".box");

let i = 0;

function move() {

box.innerHTML = ++i;

}

// 使用lodash实现节流

// box.addEventListener("mousemove", _.throttle(move, 800));

// 使用lodash实现防抖

box.addEventListener("mousemove", _.debounce(move, 800));

#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值