1.函数式组件和类组件区别?
- 无法使用State,也无法使用组件的生命周期方法。
- 接收Props,渲染DOM,,这就决定了函数组件都是展示性组件,而不关注其他逻辑。
- 没有this
- 性能相同 blog.csdn.net/xuchaobei12…
2.refs
-
react
Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素
-
vue ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
3.ts泛型
定义:不预先确定的数据类型,具体的类型在使用的时候才能确定。
好处:
- 函数和类可以轻松地支持多种类型,增强程序的扩展性。
- 不必写多条函数重载,冗长的联合类型声明,增强代码可读性。
- 灵活控制类型之间的约束。
- 泛型函数:
// 定义
function log<T> (value:T):T{
console.log(value);
return value;
}
// 调用
log<string[]>(['a','b'])
log(['a','b'])
// 使用类型别名 定义一个泛型 函数类型
type Log = <T>(value:T)=>T
let myLog :Log=log;
- 泛型接口:
interface Log <T> {
(value:T):T
}
let myLog:Log=log;
myLog('1');
- 泛型类: // 泛型类 不能约束类的静态成员 static
class Log<T>{
run(value:T){
console.log(value);
return value;
}
}
let log1 = new Log<number>()
log1.run(1);
let log2 = new Log();
log2.run('1');
interface length{
length:number
}
function log<T extends Length>(value:T):T{
console.log(value,value.length);
return value;
}
实际应用:用来创建可重用的组件,一个组件可以支持多种数据类型数据,组件不仅支持当前数据类型,还支持未来数据类型,为大型应用系统提供了十分灵活的功能
4.ts接口
接口:就是函数的一个参数的类型罢了(对象类型接口),函数类型接口 就是定义一个函数,然后类型是函数类型的接口 接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约
- 对象类型接口
interface List{
id:number;
name:string;
age?:number;
}
interface Result{
data:List[];
}
function render(result:Result){
result.data.forEach((value)=>{
console.log(value.id,value.name);
})
}
let result = {
data:[
{id:1,name:'A'},
{id:2,name:'B'},
]
}
render(result)
- 函数类型接口
type Add = (x:number,y:number) => number
let add : Add = (a,b) => a+b
// 混合类型的接口
interface Lib {
():void;
version:string;
doSomething():void;
}
function getLib(){
let lib:Lib=(()=>{}) as Lib;
lib.version = "1.0.0";
lib.doSomething=()=>{};
return lib;
}
let lib1=getLib();
lib1();
lib1.doSomething();
let lib2 = getLib();
5.es5继承和es6继承区别
- 1.ES5先创建子类,在实例化父类并添加到子类this中
- 2.ES6先创建父类,在实例化子集中通过调用super方法访问父级后,在通过修改this实现继承
- es5继承:实质是先创建子类元素child的实例对象,然后再把父类元素parent的原型对象中的属性赋值给了子类元素chid的实例对象里面,从而实现继承。
- 1.原型继承 :利用call和apply继承this上面的属性和方法
- 2.原型链继承:将父类的实例作为子类的原型
- 3.组合继承:利用call apply 继承属性 利用原型链继承方法
- es6继承:es6中采用extend继承
- 继承用extends,当继承后需要用super()来接收父类的constructor构造函数,否在报错,当new一个子类的时候先把参数传入子类构造函数再通过super()讲父类的构造函数引入,就可以调用父类。 原型和原型链
每个构造函数都有原型对象;每个对象都会有构造函数;每个构造函数的原型都是一个对象;那么这个原型对象也会有构造函数;那么这个原型对象的构造函数也会有原型对象;这样就会形成一个链式的结构,称为原型链
6.闭包
-
《你不知道的js》中描述当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
-
闭包就是函数调用自己作用域外的变量,或者说成调用作用域链上的变量,这种引用就叫闭包。
-
闭包的作用:引用变量的作用域。
-
闭包的用途:一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
-
应用场景:比如 一个变量触发某个事件的时候会改变值,而另一个函数根据这个值做响应变化。
-
闭包引起的内存泄漏
-
原因:闭包可以维持函数内局部变量,使其得不到释放。
-
解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对dom的引用
-
释放内存 是 变量设置为null
-
闭包引起 变量污染
-
原因:里面函数有可能改变外面变量的值,导致别的地方用到变量也被改了
function f1(){
n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
7.箭头函数this指向问题?
-
上下文里对象this指向,偶尔没有上下文对象,this就指向window
-
即使是call,apply,bind等方法也不能改变箭头函数this的指向
-
箭头函数没有上下文,也就是说箭头函数定义在哪里,this就就是定义箭头函数的父级
-
箭头函数定义在class里,所以this就是class
-
普通函数,谁执行他this就是谁,箭头函数,在哪里执行,this都是定义箭头函数的地方 m.html.cn/qa/javascri…
8.hooks
1.hook的定义:
Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
2.useState:
useState 会返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数。它类似 class 组件的 this.setState,但是它不会把新的 state 和旧的 state 进行合并
3.useEffect
useEffect 就是一个 Effect Hook,给函数组件增加了操作副作用的能力。它跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API
4.自定义hook
如果函数的名字以 “use” 开头并调用其他 Hook,我们就说这是一个自定义 Hook
5.useContext
让你不使用组件嵌套就可以订阅 React 的 Context。
6.useReducer
可以让你通过 reducer 来管理组件本地的复杂 state。
9.性能优化
首页加载图片过多问题,可以通过以下几种方式解决? 1.通过懒加载的方式处理非首屏的图片 2.对于纯色小图标可以采用iconfont的方式解决 3.对于彩色小图片采用雪碧图方式解决 background-position 属性
10.promise
- 解决回掉地狱的问题,promise 对象用于表示一个异步操作的最终完成(或失败),及其结果值
1.创造了一个Promise实例
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
2.Promise.prototype.then()
- then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function (comments) {
console.log("resolved: ", comments);
}, function (err){
console.log("rejected: ", err);
});
3.Promise.prototype.catch()
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});
- 第二种写法要好于第一种写法,理由是第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。因此,建议总是使用catch()方法,而不使用then()方法的第二个参数。
4.Promise.prototype.finally()
- 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
5.Promise.all()
const p = Promise.all([p1, p2, p3]);
-
只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
-
只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
6.Promise.race()
const p = Promise.race([p1, p2, p3]);
- 上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
7.Promise.allSettled()
- Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。该方法由 ES2020 引入。
8.Promise.any()
-
ES2021 引入了Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
-
Promise.any()跟Promise.race()方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected状态而结束。
11.数据结构和算法有了解吗?
这里面有10个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie树;10个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
栈:限定仅在表尾进行插入和删除操作的线性表。 队列:只允许在一端进行插入操作,在另一端进行删除操作的线性表。 串:由零个 或多个字符串组成的有序序列。 树:n(n>=0)个结点的有限集。 图:是由顶点的有穷非空集合和顶点之间边的集合组成
冒泡排序:
- 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
- 针对所有的元素重复以上的步骤,除了最后一个;
- 重复步骤1~3,直到排序完成。
// 冒泡排序
function maopao(){
let arr =[9,3,1,5,4,6,2,8,7];
for(let i=0;i<arr.length-1;i++){
for(let j=0;j<arr.length-1-i;j++)
{
if(arr[j]>arr[j+1]){
let temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
console.log("arr",arr)
}
// 优化版 冒泡排序
function maopao(){
let arr =[9,3,1,5,4,6,2,8,7];
for(let i=0;i<arr.length-1;i++){
let swap = false;
//初始设置交换状态为false
for(let j=0;j<arr.length-1-i;j++)
{
if(arr[j]>arr[j+1]){
let temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
swap = true;
//交换过后设置交换状态为true
console.log("arr",arr)
}
}
if (!swap) {
//如果交换状态为false,跳出循环
break;
}
}
}
// 这样如果执行某某次内层循环时没有交换,说明已经排好序了,跳出外层循环,冒泡结束。
最佳情况:T(n) = O(n) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)
选择排序:
- 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕
// 选择排序
function select(){
let arr =[9,3,1,5,4,6,2,8,7];
for(let i=0;i<arr.length-1;i++){
for(let j=i+1;j<arr.length-1;j++){
if(arr[j]<arr[i])
let temp = arr[i];
arr[i]= arr[j];
arr[j]=temp;
}
}
}
最佳情况:T(n) = O(n2) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)
thinkwon.blog.csdn.net/article/det… 快速排序
最佳情况:T(n) = O(nlogn) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(nlogn)
递归
递归函数就是直接或间接调用自身的函数,也就是自身调用自己。 需满足的两个条件
- 有反复执行的过程(调用自身)
- 有跳出反复执行过程的条件(递归出口) 经典问题:阶乘
二分查找
1.数据结构必须先排好序
举例:猜数字游戏 时间复杂度 O(log2n)
一致性Hash算法
thinkwon.blog.csdn.net/article/det…
12.防抖和节流?
防抖:触发高频事件后N秒内只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。
举例:在百度搜索时,每次输入之后都会有联想词弹出,这个控制联想词的方法就是不可能是输入框内容一改变就触发的,他一定是当你结束输入一段时间之后才触发的。
节流:高频事件触发,但在N秒内只会执行一次,所以节流会稀释函数执行的频率
举例:预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行,就像淘宝中抢购某一件热卖商品时,你不断的点击刷新或者购买,可是总有一段时间点击没有效果,这里就是节流。
13.pureComponent和FunctionComponent区别
- pureComponent:
- 这时候react引入了PureComponent概念,他会在render之前对state和props进行浅比较,若state和props都相同,则不会调用render。这个浅比较是在shouldComponentUpdate中进行的,所以,使用PureComponent时不能在用shouldComponentUpdate钩子函数了,因为会覆盖默认的钩子函数行为。
- PureComponent 最佳情况是展示组件。如果更新次数频繁的组件,使用 PureComponent 只会带来大量的比较,降低了性能。
14. React做SPA和使用Router的考虑
15. React router怎么实现的?(提到hash)
- 在 Web 前端单页应用 SPA(Single Page Application)中,路由描述的是 URL 与 UI 之间的映射关系,这种映射是单向的,即 URL 变化引起 UI 更新(无需刷新页面)
- 要实现前端路由,需要解决两个核心问题:
- 如何改变 URL 却不引起页面刷新?
- 如何检测 URL 变化了?
实现方法有两种:hash和history
hash
hash是url#及后半部分,改变url的hash部分不会引起页面刷新,主要通过hashchange事件监听 URL 的变化,
- 通过浏览器前进后退改变 URL
- 通过标签改变 URL
- 通过window.location改变URL 这几种情况改变 URL 都会触发 hashchange 事件
history提供popstate监听 history 提供了 pushState 和 replaceState 两个方法,这两个方法改变 URL 的 path 部分不会引起页面刷新。 通过浏览器前进后退改变 URL 时会触发 popstate 事件,通过pushState/replaceState或标签改变 URL 不会触发 popstate 事件。好在我们可以拦截 pushState/replaceState的调用和标签的点击事件来检测 URL 变化,所以监听 URL 变化可以实现,只是没有 hashchange 那么方便
16. 箭头函数和一般function的异同
-
箭头函数不能作为构造函数,不能使用new关键字
-
this的指向不同:
ES5函数的this取决于谁调用,那么this就指向谁; ES6箭头函数本身没有this,箭头函数的内部this,指向了其外层作用域,谁定义了函数,this就指向谁。也就是说对于箭头函数来说,并没有自己的 this ,它的 this 将始终指向让它生效的对象,即它的外部调用者。
-
箭头函数没有arguments参数
-
箭头函数不能使用call,apply,bind更改this指向
-
不可以使用yield命令,箭头函数不能作为generator函数
-
箭头函数不具有prototype原型对象。
-
箭头函数不具有super。
-
箭头函数不具有new.target
原文链接:blog.csdn.net/leelxp/arti…
17. Promise.all()如何实现的?口述设计思路
let p1=new Promise(resolve=>resolve('p1'));
let p2=new Promise(resolve=>resolve('p2'));
let p3=Promise.reject('p3 promise');
function promiseAll(promises){
return new Promise ((resolve,reject)=>{
let resultCount = 0;
let results = new Array(promises.length);
for(let i=0;i<promises.length;i++){
promises[i].then(item=>{
resultCount++;
results[i]=item;
if(resultCount==promises.length){
return resolve(results);
}
},error=>{
return reject(error)
})
}
})
}
promiseAll([p1,p2,p3]).then(result=>{
console.log('111',result)
}).catch(error=>{
console.log(error)
})
18. call、apply、bind的区别
- call和apply都是第一个参数是this的指向,只是传参的方式不同
- call是 第二个以及第三依次类推都是当前函数的参数,每个参数用逗号相隔开而apply则是将所有的参数以数组的形式来传递的
- call(window,参数1,参数1,参数3)
- call和apply与bind的区别
- call和apply相当于立即执行函数,使用时直接调用,而bind则类似于函数,使用时需要加()调用
- 同时bind传参方式与call相同,参数都是一个一个传递的
- Cookie(被我打个岔,打忘记了回答了)
20. XSS和CSRF的防范
1>什么是xss攻击?
- XSS 攻击指的是跨站脚本攻击,是一种代码注入攻击。攻击者通过在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 cookie 等 2>xss的防范?
一个个是恶意代码提交的时候(因应用场景无法确定,不建议所有数据都进行转译处理,不建议此方案),一个是浏览器执行恶意代码的时候。
- 使用纯前端的方式,不用服务器端拼接后返回
- 对需要插入到 HTML 中的代码做好充分的转义
- 比如使用 CSP,CSP 的本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行,从而防止恶意代码的注入攻击。
- cookie 使用 http-only 使得脚本无法获取
- 也可以使用验证码,避免脚本伪装成用户执行一些操作。
3>什么是CSRF攻击?
CSRF 攻击指的是跨站请求伪造攻击,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求。如果用户在被攻击网站中保存了登录状态,那么攻击者就可以利用这个登录状态,绕过后台的用户验证,冒充用户向服务器执行一些操作。
4>如何防止CSRF攻击? 1.同源检测的方法,服务器根据 http 请求头中 origin 或者 referer 信息来判断请求是否为允许访问的站点 2.使用 CSRF Token 来进行验证 3.使用双重 Cookie 验证的办法 4.使用在设置 cookie 属性的时候设置 Samesite,是严格模式
21. CSS的权重,结合实例具体分析
- 优先级:
- 规则:!important>内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器
- 第一个等级是行内样式,为1000,第二个等级是id选择器,为0100,第三个等级是类选择器、伪类选择器和属性选择器,为0010,
- 第四个等级是元素选择器和伪元素选择器,为0001
22. 未知宽高的元素居中(准备了,但是没答好)
23. 盒子模型,如果设置width=100px,那么盒子中各个部分的宽度多少
24. 如果设置background-color: red,那么盒子模型各个部分的背景是什么情况?
25. CSS的position定位及区别(说到和文档流的关系)
- relative:元素的位置相对于它原来的位置进行移动,元素仍占据原来的空间。
- absolute:元素的位置相对于最近的已定位的父元素,如果元素没有已定位的父元素,那么它的位置相对于。absolute定位使元素的位置与文档流无关,因此不占据空间。
- fixed:元素的位置相对于浏览器窗口是固定位置,即使窗口是滚动的它也不会移动。fixed定位使元素的位置与文档流无关,因此不占据空间
- static 自动定位,自动定位就是元素在页 面普通文档流中由HTML自动定位,普通文档里中的元素也称为流动元素
26. HTTP和HTTPS的区别
- https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
- http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
- http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
- http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全
27. JS垃圾回收机制(这点面试官说JS的回收机制其实很多,我说到的只是其中的一个算法)
标记清除 引用记述
28. TCP和UDP的区别
-
TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
-
TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
-
TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
-
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
-
每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
-
TCP首部开销20字节;UDP的首部开销小,只有8个字节
-
TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
29. 进程和线程的区别
- 进程是资源分配的最小单位,线程是程序执行的最小单位。
- 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间
- 而线程是共享进程中的数据的,使用相同的地址空间
- 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行
- 但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。 blog.csdn.net/qq_40574571…
30. 如果需要对数据进行插入和删除,什么数据结构最快
31. 如果还需要考虑数据的可索引性质,什么数据结构最好
32. 输入一个URL整个流程介绍
-
对www.baidu.com 网址进行DNS域名解析,得到对应的ip地址。
-
根据这个ip,找到对应的服务器,发起tcp的三次握手。
-
建立tcp连接后发起http请求{tcp是比http更底层的一个连接协议}(ip是tcp下面一层)
-
服务器响应http请求,浏览器得到html代码
-
浏览器解析html代码,并请求html代码中的资源(如js、css、图片等)
-
(先得到这些html代码,才能去找到这些资源)
-
浏览器对页面进行渲染呈现给用户
-
服务器关闭tcp连接
-
回答思路:
-
先说从url拿到html的过程,然后重点阐述 html的渲染过程,之后面试官再次提问测重回答(如:重排,重绘,tcp 3次握手 四次挥手)
33. DNS解析时,有什么算法和方式减少重复操作
34. var let const 不写的区别
- var声明的变量会挂载在window上,而let和const声明的变量不会
- var声明变量存在变量提升,let和const不存在变量提升
- let和const声明形成块作用域
- 同一作用域下let和const不能声明同名变量,而var可以
- const一旦声明必须赋值,不能使用null占位;声明后不能再修改 ;如果声明的是复合类型数据,可以修改其属性
35.promise原理
36.typeof 判断数据类型
let arr=[];
let a=1;
let b="";
let c=true;
let d={};
let e=null;
let f=undefined;
console.log(arr, typeof arr); //[] "object"
console.log(a, typeof a);// 1 "number"
console.log(b, typeof b);// string
console.log(c, typeof c);// true "boolean"
console.log(d, typeof d); // {} "object"
console.log(e, typeof e);// null "object"
console.log(f, typeof f);// undefined "undefined"
37.使用instanceof
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 在这里需要特别注意的是:instanceof 检测的是原型, instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型
console.log(true instanceof Boolean);// false
console.log(1 instanceof Number);// false
console.log('abc' instanceof String);// false
console.log(undefined instanceof Object);// false
console.log([1,2,3]] instanceof Array);// true
console.log(null instanceof Object);// false
console.log({name:"hao"}} instanceof Object);// true
console.log(function(){} instanceof Function);// true
var bool2 = new Boolean()
console.log(bool2 instanceof Boolean);// true
var num2 = new Number()
console.log(num2 instanceof Number);// true
var str2 = new String()
console.log(str2 instanceof String);// true
function Person(){}
var per = new Person()
console.log(per instanceof Person);// true
function Student(){}
Student.prototype = new Person()
var haoxl = new Student()
console.log(haoxl instanceof Student);// true
console.log(haoxl instanceof Person);// true
从结果中看出instanceof不能区别undefined和null,而且对于基本类型如果不是用new声明的则也测试不出来,对于是使用new声明的类型,它还可以检测出多层继承关系。
38、使用constructor
constructor是原型prototype的一个属性,当函数被定义时候,js引擎会为函数添加原型prototype,并且这个prototype中constructor属性指向函数引用, 因此重写prototype会丢失原来的constructor。 undefined和null没有contructor属性
console.log(true.constructor === Boolean);// true
console.log(1.constructor === Number);// true
console.log("abc".constructor === String);// true
console.log([1,2,3]].constructor === Array);// true
console.log({name:'hao'}.constructor === Object);// true
console.log(function(){}.constructor === Function);// true
console.log(haoxl.constructor === Student);// false
console.log(haoxl.constructor === Person);// true
constructor不能判断undefined和null,并且使用它是不安全的,因为contructor的指向是可以改变的
39.toString 是最完美的
toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。 对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用
40.for...of
Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环