js、es6面试题:
(1)JS的数据类型
-
基础数据类型
String
、Number
、Boolean
、Undefined
、Null
、es6新增两种(Symbol
、BigInt
) -
引用数据类型:
Object
、Array
、Function
、Date
等 -
Number类型包括整数和浮点数(小数),NaN表示Not A Number,检查时返回number。如果使用Number表示的数字超过了最大值,则会返回Infinity(正无穷)
-
String类型数据通常由单引号或双引号包裹,可以双包单或单包双,单引号和双引号不能混用和嵌套,可以使用\进行转义。
-
Boolean只有两个true/false。
-
Undefined 型(未定义)也是只有一个 undefined 值。 undefined 代表了某个变量完全不存在,在内存中完全不存在这个标识符所指向的地址。
-
Null类型(空值型)只有一个 null值,null 值本质上是一个空指针的对象类型。 null 代表了内存中是存在这个变量的,只是我在某些情况下需要把这个变量原本的值给覆盖了,将它设置为一个空。
-
BigInt可以表示任意精度的整数,可以操作超过
Number.MAX_SAFE_INTEGER
的数字 -
Symbol表示独一无二的值,
引用数据类型和基本数据类型的区别:
- 基本数据类型存储在栈中,操作频繁,内存占用少,大小固定
- 引用数据类型存储在堆中,内存占用大,大小不固定,根据用户操作动态变化,声明一个引用类型的变量保存引用数据的地址。存在数据共享性问题。
undefined出现的场景:
- 变量被声明了,但是没有被赋值;
- 调用函数的时候,该传的参数没有传;
- 访问一个对象中没有的属性;
- 函数没有返回值时,默认就会返回undefined。
null出现的场景:
- 作为对象原型链的终点出现;
- 访问一个不存在的dom节点。
(2)判断数据类型的方法
typeof | 判断基础数据类型(数组、对象、null都会被判断为object) |
---|---|
instanceof | 判断引用数据类型,不能判断基本数据类型 |
constructor | 判断两种数据的类型,弊端(如果声明了一个构造函数,并把原型指向Array,此时将无法判断) |
Object.prototype.toString.call() | 使用 Object 对象的原型方法 toString 来判断数据类型(完美解决上面三种方式存在的问题) |
(3)es6新增特性
- 新增块级作用域let、const关键字
- 解构赋值、模块化(import,export)、扩展运算符、模版字符串、
- 箭头函数
- 新增数据类型和数据结构Symbol、BigInt、Set、Map
- 新增异步promise、async/await
- 新增Proxy、Reflect、Generator、Iterator
- 类和继承
- 字符串、数组、对象的方法扩展
(4)let、const、var的区别
- const 定义常量, 不可以重复赋值 ,块级作用域 ,不存在变量提升
- var 定义变量,可以重复声明 var 全局作用域或函数作用域,有变量提升
- let 定义变量,不可以重复声明 , 块级作用域 ,不存在变量提升
(5)箭头函数的特点
- 箭头函数没有自己的this,它的this继承自父级作用域且指向永远不变
- 箭头函数不能作为构造函数使用
- 箭头函数没有自己的arguments
- 箭头函数没有prototype
(6)JS由那几部分组成
- 由三部分组成时:ECMAScrip(核心)、DOM(文档流对象)、BOM(浏览器对象模型)
- 由两部分组成时:ECMAScrip(核心)、WEB AIP
(7)构造函数和new
- 构造函数:用来初始化对象,即为对象成员变量赋初始值,总与new一起使用,可将对象中公共的属性和方法取出来封装到该函数中。
- 构造函数中的属性和方法称为成员。构造函数中可以再构造函数本身上添加静态成员,也可以在狗仔函数内部的this上添加实例成员。(其中在哪儿添加只能从哪儿访问)
- new操作具体做了什么
①在内存中先创建一个空对象
②让this指向该空对象
③执行构造函数的代码,给新对象添加属性和方法
④返回该新对象(构造函数中不需要return)
(8)原型对象(prototype)、对象原型(_ _ proto_ _)、原型链
-
原型对象:每一个构造函数都有一个prototype属性(原型对象),指向另一个对象。构造函数拥有prototype对象的所有属性和方法。原型的作用是实现共享方法。
(注意:一般情况将公共属性定义到构造函数中,公共方法定义到原型对象上。)
-
对象原型:对象都有_ _ proto_ _属性指向构造函数的prototype原型对象,故我们可以使用prototype原型对象上的属性和方法。(原型对象和对象原型是等价的)
-
原型链:一个实例对象在调用属性和方法时,会一次从实例对象本身到构造函数,再到原型的原型上去查找,直到找到Object为止,Object的原型是null。这种链式查找的过程称为原型链。
(9)作用域、作用域链
-
作用域:指程序定义变量的区域,他决定了当前代码对变量的访问权限。
-
JS有三种作用域分别为全局作用域、函数作用域、块级作用域
- 全局作用域:在代码最外层作用域一直存在,在代码任何地方都能访问。
- 函数作用域:定义在函数中的变量,函数每次被调用都拥有一个不同的作用域。
- 块级作用域:在es6中新增了let、const命令,可以用来创建块级作用域,只在其代码作用域中有效。
-
作用域链:当代码内部访问变量时,会先查找本地作用域,如果目标变量找到即返回,否则去父级作用域中查找,一直找到全局作用域,这种作用域嵌套机制就是作用域链。
(10)闭包
- 闭包:函数嵌套函数,内部函数可以访问外部函数的变量和参数,内部函数作为外部函数的返回值,再将函数赋值给一个变量保存下来就会产生闭包。
- 特点: ①可以重复利用变量,该变量不会污染全局②该变量会一直保存在内存中,不会被垃圾回收机制回收
- 缺点:闭包过多会消耗内存,页面性能下降,造成内存泄漏
- 使用场景:防抖、节流、函数嵌套、函数避免全局污染
(11)防抖和节流
- 防抖:单位之间内,频繁触发事件,只执行最后一次。(重新开始)
- 应用场景:①搜索框搜索输入②文本编辑器实时保存
- 代码(使用定时器)
let timerId = null
document.querySelector('.pt').onkeyup=function (){
if(timerId!==null){
clearTimeOut(timerId)
}
timerId=setTimeout(()=>{
console.log("我是防抖")
},1000)
}
-
节流:单位之间内,频繁触发事件,只执行一次函数。(不会重新开始)
-
应用场景:高频事件(快速点击、鼠标滑动、resize、scroll)、下拉加载、视频播放记录时间
-
代码
let timerId = null document.querySelector('.pt').onmouseover=function (){ if(timerId!==null){ return } timerId=setTimeout(()=>{ console.log("我是节流") timerId=null },1000) }
(12)js中的堆内存与栈内存
栈内存:
- 存储基本数据类型的值和引用类型的变量的引用。
- 按值访问
- 大小固定,按照"后进先出"的顺序进行操作。
- 有系统自动分配内存空间
- 空间小,运行效率高
- 自动分配和释放,变量作用域结束时自动销毁。
堆内存:
- 存储引用类型的对象,如数组和对象。
- 通过引用地址查找对象。
- 大小不固定,无需存储。动态分配和释放。
- 有代码进行指定分配
- 空间大,运行效率相对校低
- 需要手动管理对象的创建和销毁。
(13)JS垃圾回收机制
垃圾回收机制:自动管理内存机制,用于清除不再使用的对象。
策略:引用计数法、标记清除法、分代回收
标记清除(Mark and Sweep):(常用)
- 当变量进入执行环境时,垃圾回收器会标记这些变量为"存活"。
- 通过根变量(如全局变量)开始遍历内存中的对象,标记所有从根变量可以访问到的对象为"存活"。
- 未被标记的对象即为"垃圾",将被回收并释放内存空间。
引用计数(Reference Counting):(较少使用)
- 当一个对象被引用一次则引用数值+1,被取消引用一次则引用数-1
- 当引用计数为0时,说明该对象不再被引用,即为"垃圾",将被回收。
- 该算法存在循环引用的问题,即使对象不再被使用,但互相引用的对象的引用计数仍不为0,导致内存无法释放。
分代回收(Generational Collection):(改进算法)
- 根据对象的存活时间将内存分为不同的代(Generation)。
- 大部分新创建的对象被认为是临时对象,存活时间较短,分配在较新的代中。
- 经过多次垃圾回收仍然存活的对象会逐渐晋升到较老的代中,减少频繁回收的成本。
- 较老的代中的对象会较少被扫描,提高垃圾回收的效率。
(14)内存泄漏
内存泄漏:不再使用的对象保留在内存中,导致内存占用不断增加而无法释放。
内存泄漏因素:
- 闭包(手动清除,局部变量设置为null)
- 定时器和回调函数未清除(解决:在定时器完成工作时手动清除)
- 未清理DOM元素的引用(解决:手动清除存储DOM的变量为null)
- 全局变量(解决:在函数内使用严格模式"use strict",禁止this关键字指向全局变量)
(15)js事件循环中的宏任务和微任务
-
宏任务:setTimeout、setInterval、DOM事件、AJAX事件
-
微任务:Pomise、async/await
-
微任务 > DOM渲染 > 宏任务
(16)同步和异步的理解
- 同步:只有前一个任务执行完毕,才能执行后一个任务。
- 异步:当同步任务执行到某个 WebAPI 时,就会触发异步操作,此时浏览器会单独开线程去处理 这些异步任务。
(17)Promise
Promise:解决毁掉地域问题
-
实例方法: then、catch、finally 三种,
-
静态方法: all、race、allSettled、any、resolve、reject 六种
-
Promise.prototype.then()
(核心)语法:promise.then(onFulfilled, onRejected)
当 handler 返回一个正常值的时候,这个值会传递给 Promise 对象的 onFulfilled 方法。定义的 handler 中产生异常的时候,这个值则会传递给 Promise 对象的 onRejected 方法。
-
Promise.prototype.catch()
Promise 对象的 catch 方法来捕获异步操作过程中 出现的任何异常。
不建议在 Promise 内部使用 throw 来触发异常,而是使用 reject(new Error()) 的方式来做,因为 throw 的方式并没有改变 Pronise 的状态
-
Promise.prototype.finally()
不管是 resolve 还是 reject 都会调用 finally ,可以在这里关闭使用的对象
-
Promise.all()
参数传递 promise数组中所有的 Promise 对象都变为resolve的时候,该方法才会返回。
如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的 新的 Promise 对象。
由于参数数组中的每个元素都是由 Promise.resolve 包装(wrap)的,所以Promise.all 可以处理不同 类型的 Promise 对象。
如果全部成功返回一个顺序排列的数组,如果失败则实例第一个态变为rejected的拒因
当参数为空时同步的返回一个已完成状态的promise, 终值值一个空数组
-
Promise.race()
返回最快完成那一个 Promise 实例,参数 promise 数组中的任何一个 Promise 对象如果变为 resolve 或者 reject 的话, 该函数就会返回, 并使用这个 Promise 对象的值进行 resolve 或者 reject。
当参数为空返回一个pending状态的promise
-
Promise.allSettled()
返回所有promise实例执行的数组,无论是执行 resolve 回调还是 reject 回调的状态。
返回一个顺序同promise的输入顺序一致的数组
当参数为空时同步的返回一个已完成状态的promise, 终值值一个空数组
-
Promise.any()
返回任意一个最快执行 resolve 回调的 Promise 实例。
当参数为空同步的返回一个失败状态的promise,拒因是一个
AggregateError
对象 -
Promise.resolve()
返回值也是一个 Promise 对象,所以可以接着对其返回值进行 .then 调用。
Promise.resolve 是 new Promise() 的快捷方式。
-
Promise.reject()
Promise.reject(error) 是和 Promise.resolve(value) 类似的静态方法,是 new Promise() 方法的快捷方 式。
-
给出ajax,写出promise
function getPromise(url) {
return new Promise((resolve, reject) => {
ajax(url, res => {
resolve(res)
}, err => {
reject(err)
})
})
}
手写promise:
// promise
// 状态会多次用于判断,所以建议用常量保存
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class Promise{
constructor(handler){
// 构造函数需要参数,参数必须是一个函数类型 这里面我们叫做handler,这儿会对handler类型进行验证,如果不是函数类型会抛出类型异常
if(typeof handler !== 'function'){
throw new TypeError(`Promise构造函数的参数${handler}不是一个函数`)
}
this.state = PENDING // 用来存储promise的状态 初始化状态为pending
this.reason = undefined // 拒因
this.value = undefined // 终值
// resolve,reject方法里面会用到实例的this,在调用的时候需要改变this的指向
handler(this.resolve.bind(this), this.reject.bind(this))
}
resolve(value){
// 该方法只能在promise状态为pending的时候调用,如果promise已经有了状态,则不允许调用
if(this.state !== PENDING) return
// 调用方法promise的状态变为fulfilled
this.state = FULFILLED
// 调用该方法会得到一个终值value
this.value = value
}
reject(reason){
// 该方法只能在promise状态为pending的时候调用,如果promise已经有了状态,则不允许调用
if(this.state !== PENDING) return
// 调用方法promise的状态变为rejected
this.state = REJECTED
// 调用该方法会得到一个拒因reason
this.reason = reason
}
}
(18)Async/Await
语法:
async function f(){
const a=await fetch('http://...');
}
陷阱:
①当有两个异步时,使用all组合更高效
async function f(){
const promiseA=await fetch('http://...');
const promiseB=await fetch('http://...');
const[a,b]=await Promise.all([promiseA,promiseB]);
}
②循环中执行异步操作时,可使用传统的for循环,可以用for await 使循环中的异步并发执行。
async function f(){
const promises=[
someAsyncOperation(),
someAsyncOperation(),
someAsyncOperation(),
]
for await(let res of promises){
//操作
}
}
③await必须搭配async一起使用
(19)手写ajax
function ajax(url,method,callback) {
let xmlhttp
if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera,
Safari
xmlhttp = new XMLHttpRequest()
} else { // code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP")
}
// 发送请求
xmlhttp.open(method, url, true)
xmlhttp.send()
// 服务端响应
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// console.log(xmlhttp.responseText)
let obj = JSON.parse(xmlhttp.responseText)
callback(obj)
}
}
}
promise和async/await的区别:
-
都是处理异步请求方式,都是非阻塞性的
-
promise通过then、catch处理和捕获异常、链式调用代码重叠不好维护,async/await通过try/catch捕获异常
-
async/await遇到await会等待返回结果在执行后续操作、promise.then()可能会出现请求还未返回就进行后续操作的情况。
(20)隐式转换
- 加(+):
- 当一侧为
String
类型,被识别为字符串拼接,并会优先将另一侧转换为字符串类型。 - 当一侧为
Number
类型,另一侧为原始类型,则将原始类型转换为Number
类型。 - 当一侧为
Number
类型,另一侧为引用类型,将引用类型和Number
类型转换成字符串后拼接。
- 当一侧为
- 减、乘、除
- 在对非
Number
类型运用数学运算符(-
、*
或/
)时,会先将非Number
类型转换为Number
类型再进行计算。
- 在对非
- 单个变量
- 如果只有单个变量,会先将变量转换为
Boolean
值。只有null
、undefined
、''
、NaN
、0
、false
这几个会被转换为false
,其他的情况都是true
,比如{}
,[]
等
- 如果只有单个变量,会先将变量转换为
- 逻辑运算符==
NaN
和任何类型比较永远返回false
(包括它本身)。Boolean
和其他任何类型比较,Boolean
首先被转换为Number
类型。String
和Number
比较,先将String
类型转换为Number
类型。null == undefined
比较结果是true
,除此之外,null
、undefined
和其他任何类型的比较都为false
。原始类型
和引用类型
比较时,引用类型调用对象的valueOf
和toString
方法,将参数转换为原始类型。
(21)JS中的this
优先级:new > 显示 > 隐式 > 默认
this绑定规则:(在运行时绑定,取决于函数的调用位置)
-
默认绑定
- 当函数不使用任何修饰符如call、apply、new等时,默认绑定全局
- 当使用严格模式时,不能绑定到全局作用域。
-
隐式绑定
- 调用位置是否有上下文对象,如果有则指向该对象
- 对象属性引用链中只有最后一层灰影响调用位置
- 存在隐式缺失
-
显示绑定
-
通过
call
、apply
、bind
绑定 -
①第一个参数都是this要指向的对象,如果没有这个参数或参数为undefined或null,则默认指向全局window
-
②call是参数列表,apply是数组,call和apply是一次性传入参数
tFn.call(test(obj,args1,args2,args3)) tFn.apply(test(obj,[args1,args2,args3]))
-
③bind是参数列表,但可以分为多次传入参数
function bind(fn,obj){ return function(){ return fn.apply(obj,arguments); } }
-
④bind是返回绑定this之后的函数,apply、call 则是立即执行
-
如果传入的值四飞引用类型的值,那么该值将自动被转化成它的对象形式,例如new String()。
-
-
new绑定
- 创建一个空对象
- 将对象与原型进行链接
- 将新对象绑定到函数调用的this
- 如果函数没有返回其他对象,则new表达式中的函数将返回该对象
-
箭头函数中的this向外层一级一级查找作用域中的this
(22)JS中ES6数据结构Map 、Set 、WeakMap 、 WeakSet
Set:
- Set是一种存储唯一值的集合,无重复值,值可以是任意数据类型。
Array.from
方法可以将 Set 结构转为数组- Set 结构的实属性:
Set.prototype.constructor
:构造函数,默认就是Set
函数。Set.prototype.size
:返回Set
实例的成员总数。
- Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)
- Set的操作方法:
Set.prototype.add(value)
:添加某个值,返回 Set 结构本身。Set.prototype.delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。Set.prototype.has(value)
:返回一个布尔值,表示该值是否为Set
的成员。Set.prototype.clear()
:清除所有成员,没有返回值。
- Set的遍历方法:
Set.prototype.keys()
:返回键名的遍历器Set.prototype.values()
:返回键值的遍历器Set.prototype.entries()
:返回键值对的遍历器Set.prototype.forEach()
:使用回调函数遍历每个成员for...of
:可以直接遍历每个成员
WeakSet:
- WeakSet是一种特殊的Set,其中的值只能是对象。
- WeakSet 没有size属性,也没有迭代器,没有办法遍历它的成员。
- WeakSet 中的对象都是弱引用,不会阻止对象被垃圾回收。
- WeakSet 的方法
WeakSet.prototype.add(value)
:向 WeakSet 实例添加一个新成员。WeakSet.prototype.delete(value)
:清除 WeakSet 实例的指定成员。WeakSet.prototype.has(value)
:返回一个布尔值,表示某个值是否在 WeakSet 实例之中。
Map:
- Map是一种键值对的集合,其中每个键只能出现一次。键可以是任意数据类型(包括原始类型和对象),值也可以是任意数据类型。
- Map实例属性:
Map.prototype.size
:返回Map
实例的成员总数。
- Map操作方法:
Map.prototype.set(key, value)
:set
方法设置键名key
对应的键值为value
,然后返回整个 Map 结构。如果key
已经有值,则键值会被更新,否则就新生成该键。可以采用链式写法Map.prototype.get(key)
:get
方法读取key
对应的键值,如果找不到key
,返回undefined
。Map.prototype.has(key)
:has
方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。Map.prototype.delete(key)
:delete
方法删除某个键,返回布尔类型。Map.prototype.clear()
:clear
方法清除所有成员,没有返回值。
- Map遍历方法(返回的顺序都与插入顺序一致)
Map.prototype.keys()
:返回键名的遍历器。Map.prototype.values()
:返回键值的遍历器。Map.prototype.entries()
:返回所有成员的遍历器。Map.prototype.forEach()
:遍历 Map 的所有成员。for...of
: 可以直接遍历每个成员
WeakMap:
- WeakMap只接受对象作为键名(
null
除外),值可以是任意类型。 - WeakMap中的键是弱引用,不会阻止对象被垃圾回收
- WeakMap没有size属性,无法遍历
Set和Map区别:
- Set是一种无序且不重复的集合,可以用来存储任何类型的值。Map是一种键值对的集合,可以用来存储任何类型的键和值。
- Map可以通过get方法获取值,而set不能(它只有值);
- 都能通过迭代器进行for…of遍历;
- Set常做数组去重,Map可做数据存储。
(23)深拷贝浅拷贝
浅拷贝:浅拷贝是对象共用的一个内存地址,对象的变化相互印象。
深拷贝:深拷贝是将对象放到新的内存中,两个对象的改变不会相互影响。
实现浅拷贝的方法:
-
Object.assign()
- Object.assign(target, …sources)
- 用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,它将返回目标对 象。
- 不会拷贝对象继承、不可枚举、属性的数据/访问器属性,可以拷贝Symbol类型。
-
遍历:
function deepClone(source) { if (!source || typeof source !== 'object') { return source; } const targetObj = source.constructor === Array ? [] : {}; for (const keys in source) { if (source.hasOwnProperty(keys)) { targetObj[keys] = source[keys]; } } return targetObj; }
-
使用拓展运算符实现的复制([…])
实现深拷贝的方法:
-
JSON.parse(JSON.stringfy()) 把对象转成字符串,返回新生成的字符串,然后在通过JSON.parse转成JSON格式,在赋给新的对象
弊端:具有循环引用的对象时,报错;当值为函数、undefined、或symbol时,无法拷贝。
-
递归拷贝:在浅拷贝的遍历方法上多加一层判断,进而对所有层级的元素进行遍历赋值
function deepClone(source) { if (!source || typeof source !== 'object') { return source; } const targetObj = source.constructor === Array ? [] : {}; for (const keys in source) { if (source.hasOwnProperty(keys)) { if (source[keys] && typeof source[keys] === 'object') { targetObj[keys] = source[keys].constructor === Array ? [] : {}; targetObj[keys] = deepClone(source[keys]); } else { targetObj[keys] = source[keys]; } } } return targetObj; }
(24)操作数组的方法有哪些
map | 遍历数组,返回回调返回值组成的新数组 |
---|---|
forEach | 无法break,可以用try/catch中throw new Error来停止 |
filter | 过滤 |
some | 有一项返回true,则整体为`true |
every | 有一项返回false,则整体为`false |
join | 通过指定连接符生成字符串 |
push / pop | 末尾推入和弹出,改变原数组。push 返回数组长度, pop 返回原数组最后一项 |
unshift / shift | 头部推入和弹出,改变原数组,unshift 返回数组长度,shift 返回原数组第一项 ; |
sort(fn) / reverse | 排序与反转,改变原数组 |
concat | 连接数组,不影响原数组, 浅拷贝 |
slice(start, end) | 返回截断后的新数组,不改变原数组 |
splice(start, number, value…) | 返回删除元素组成的数组,value 为插入项,改变原数组 |
indexOf / lastIndexOf(value, fromIndex) | 查找数组项,返回对应的下标 |
reduce / reduceRight(fn(prev, cur), defaultPrev) | 两两执行,prev 为上次化简函数的return值,cur 为当前值 当传入 defaultPrev 时,从第一项开始; 当未传入时,则为第二项 |
(25)JS中的常用循环及区别
-
for循环(常见基础)
-
可以及时break出循环
for (初始化表达式; 条件表达式; 更新表达式) { // 循环体 }
-
-
for…in 循环
-
用于遍历
对象
的属性(如果遍历数组,得到的是数组的索引)for (let 变量 in 对象) { // 循环体 }
-
-
for…of 循环
-
es6新增用于遍历数组等可迭代对象中的所有元素,如Set,Map,String,Array
for (let 变量 of 可迭代对象) { // 循环体 }
-
-
forEach
-
与map相同用于遍历数组的每个元素。forEach方法不返回值,只用来操作数据,且循环中途是无法停止的,总是会将所有成员遍历完
array.forEach((currentValue, index, array)=>{ // 循环体 });
-
-
map
-
map方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。循环中途是无法停止的,总是会将所有成员遍历完。
-
map方法接受一个函数作为参数
-
map方法向它传入三个参数:当前成员、当前位置和数组本身。
array.map((currentValue, index, array)=>{ // 循环体 });
-
-
filter
-
用于遍历数组并返回一个包含符合条件元素的新数组。
const newArray = array.filter(function(currentValue, index, arr) { // return true or false });
-
(26)JS的继承
-
原型链继承
-
让一个构造函数的原型是另一个类型的实例,则该构造函数new出来的实例就具有该实例的属性
-
子类的原型等于父类的实例
function Parent() { this.isShow = true this.info = { name: "tom", age: 18, }; } function Child() {}; Child.prototype = new Parent();
缺点:对象实例共享所有继承的属性和方法,无法向父类构造函数传参
-
-
借助构造函数继承
-
在子类型的构造函数的内部调用父类的构造函数,通过 apply,call 方法将父类构造函数绑定在子类对象上。
function Parent(gender) { this.info = { name: "tom", age: 19, gender: gender } } function Child(gender) { Parent.call(this, gender) }
优点:解决原型链继承无法传参问题
缺点:方法都在构造函数中定义,故无法实现函数复用,父类原型中定义的方法子类不可见,只能使用构造函数模式
-
-
组合式继承(经典)
-
将
原型链
和借用构造函数
的组合。使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。function Person(gender) { this.info = { name: "tom", age: 19, gender: gender } function Child(gender) { Person.call(this, gender) // 使用构造函数法传递参数 } Child.prototype = new Person()
-
-
Class类继承(es6新增)
-
Class通过extends关键字实现继承,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this
Class Person{ constructor(ele){ this.ele=ele } getEle(){ return this.ele; } } Class Child extends Person{ constructor(name) { this.name=name } }
缺点:并非所有浏览器都支持Class
-
声明:以上内容来自书籍查阅、网络博客教学视频等内容。如有侵权请私信。