js判断类型的方法
1 | 1. typeof |
== 和 === 有什么区别?
== 如果对比双方的类型不一样的话,就会进行类型转换
=== 判断两者类型和值是否都相同
== 判断流程
- 首先会判断两者类型是否相同。相同的话就是比大小了
- 类型不相同的话,那么就会进行类型转换
- 会先判断是否在对比
null
和undefined
,是的话就会返回true
- 判断两者类型是否为
string
和number
,是的话就会将字符串转换为number
- 判断其中一方是否为
boolean
,是的话就会把boolean
转为number
再进行判断 - 判断其中一方是否为
object
且另一方为string
、number
或者symbol
,就调用object
的valueOf()方法,用得到的基本类型值按照前面的规则进行比较,如果object
没有valueOf()方法,则调用 toString() - 如果有一个操作数是
NaN
,则相等操作符返回 false
1 | '1' == { name: 'yck' } |
面试题:[] == ![]
1 | 1. [] == ![] // true |
call和apply
一、call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向;
apply和call都能继承另外一个对象的方法和属性;
1 |
|
二、对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。例如,有一个函数定义如下:
1 | var func = function(arg1, arg2) { |
其中 this 是你想指定的上下文,他可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里;
模拟实现call和apply
可以从以下几点来考虑如何实现
- 不传入第一个参数,那么默认为
window
- 改变了
this
指向,让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除?
1 | // call |
以上就是 call
的思路,apply
的实现也类似
1 | // apply |
bind
和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过 bind
实现柯里化。
同样的,也来模拟实现下 bind
1 | // bind |
对闭包的理解
红宝书(p178)上对于闭包的定义:闭包是指有权访问另外一个函数作用域中的变量的函数。
MDN 对闭包的定义为:闭包是指那些能够访问自由变量的函数。 (其中自由变量,指在函数中使用的,但既不是函数参数arguments也不是函数的局部变量的变量,其实就是另外一个函数作用域中的变量。)
口述理解:函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。
闭包的表现形式
1 |
|
如何解决下面的循环输出问题?
1 |
|
为什么会全部输出6?如何改进,让它输出1,2,3,4,5?(方法越多越好)
因为setTimeout为宏任务,由于JS中单线程eventLoop机制,在主线程同步任务执行完后才去执行宏任务,因此循环结束后setTimeout中的回调才依次执行,但输出i的时候当前作用域没有,往上一级再找,发现了i,此时循环已经结束,i变成了6。因此会全部输出6。
解决方法:
1 |
|
对原型链的理解
原型对象和构造函数有何关系
在JavaScript中,每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象。
当函数经过new调用时,这个函数就成为了构造函数,返回一个全新的实例对象,这个实例对象有一个proto属性,指向构造函数的原型对象。
原型链的描述
JavaScript通过proto指向父类对象,直到指向Object对象为止,这样就行程一个原型指向的链条,即原型链;
- 对象的 hasOwnProperty() 来检查对象自身中是否含有该属性
- 使用 in 检查对象中是否含有某个属性时,如果对象中没有但是原型链中有,也会返回 true
JS如何实现继承
组合继承
1 |
|
寄生组合继承(推荐)
1 | // 写法一: |
1 | // 写法二: |
ES6 extends关键字继承(推荐)
1 |
|
for of 与 for in 的区别
for…in循环出的是key,for…of循环出的是value
for in
1 | for(let index in aArray){ |
for of
1 | for(var value of aArray){ |
new 实现过程
- 新生成了一个对象
- 链接到原型
- 绑定this
- 返回新对象
1 | function create() { |
指定随机数范围(整数)
1 | function sum(m,n) { |
深拷贝与浅拷贝
产生的原因
1 | let a = { |
从上述例子中我们可以发现,如果给一个变量赋值一个对象,那么两者的值会是同一个引用,其中一方改变,另一方也会相应改变。
通常在开发中我们不希望出现这样的问题,我们可以使用浅拷贝来解决这个问题。
浅拷贝使用
1 | 方法一:Object.assign |
通常浅拷贝就能解决大部分问题了,但是当我们遇到如下情况就需要使用到深拷贝了
1 | let a = { |
浅拷贝只解决了第一层的问题,如果接下去的值中还有对象的话,那么就又回到刚开始的话题了,两者享有相同的引用。要解决这个问题,我们需要引入深拷贝。
深拷贝
这个问题通常可以通过 JSON.parse(JSON.stringify(object)) 来解决。
1 | let a = { |
但是该方法也是有局限性的:
- 会忽略 undefined
- 会忽略 symbol
- 不能序列化函数
- 不能解决循环引用的对象
在通常情况下,复杂数据都是可以序列化的,所以这个函数可以解决大部分问题,并且该函数是内置函数中处理深拷贝性能最快的。当然如果你的数据中含有以上三种情况下,可以使用 lodash 的深拷贝函数。
this的理解
this 是很多人会混淆的概念,但是其实他一点都不难,你只需要记住几个规则就可以了。
1 | function foo() { |
以上几种情况明白了,很多代码中的 this 应该就没什么问题了,下面让我们看看箭头函数中的 this
1 | function a() { |
箭头函数其实是没有 this 的,这个函数中的 this 只取决于他外面的第一个不是箭头函数的函数的 this。在这个例子中,因为调用 a 符合前面代码中的第一个情况,所以 this 是 window。并且 this 一旦绑定了上下文,就不会被任何代码改变。
Vue的生命周期
1 | new Vue() |
Vue中computed和watch的区别
computed 是计算属性,依赖其他属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容。
watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作
一般来说需要依赖别的属性来动态获得值的时候可以使用 computed
对于监听到值的变化需要做一些复杂业务逻辑的情况可以使用 watch。
Vue中响应式的原理
Vue 内部使用了 Object.defineProperty()
来实现数据响应式,通过这个函数可以监听到 set
和 get
的事件。
发布-订阅模式
发布-订阅模式也叫做观察者模式。通过一对一或者一对多的依赖关系,当对象发生改变时,订阅方都会收到通知
发布订阅模式核心是 addEventListener
在 Vue 中,如何实现响应式也是使用了该模式。对于需要实现响应式的对象来说,在 get 的时候会进行依赖收集,当改变了对象的属性时,就会触发派发更新。
什么是BFC
块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视化CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。
如何创建BFC
- 根元素
- 浮动元素(float 属性不为 none)
- position 为 absolute 或 fixed
- overflow 不为 visible 的块元素
- display 为 inline-block, table-cell, table-caption