1、分别用迭代和递归实现数组的扁平化 flatten
flatten([1, 2, [3, [4, [5]]]]) // [1, 2, 3, 4, 5]
解析:
// 迭代
const flatten = function (arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr)
}
return arr
}
// 递归
const flatten = arr => arr.reduce((a, b) =>
(Array.isArray(b) ? [...a, ...flatten(b)] : [...a, b]), [])
拓展
- 判断是否为数组
常见typeof判断arr instanceof Array // 对象arr是不是构造函数Arrat构造出来的 Array.isArray(arr) // ES5中数组的方法 typeof arr.slice // 判断对象的某个属性是否为函数 Object.prototype.toString.call(arr) === '[object Array]' arr.constructor === Array
typeof undefined 'undefined' typeof null 'object' typeof true 'boolean' typeof 123 'number' typeof "abc" 'string' typeof function() {} 'function' typeof {} 'object' typeof [] 'object'
- 数组reduce方法的使用 https://www.jianshu.com/p/0a3c6cbdeb55
- 数组some和every https://www.jianshu.com/p/a9fceb106b0b
- 数组和类数组的区别 https://github.com/gloves2Ali/steven-blog/blob/master/javascript/%E7%B1%BB%E6%95%B0%E7%BB%84%E5%92%8C%E6%95%B0%E7%BB%84%E7%9A%84%E5%8C%BA%E5%88%AB.md
2、改造下列函数,使之输出0-9
for (var i = 0; i< 10; i++){
setTimeout(() => {
console.log(i)
}, 1000)
}
解析:
setTimeout定义的操作在函数调用栈清空之后才会执行 https://www.jianshu.com/p/9b4a54a98660
setTimeout(function () {
console.log('aaaa')
}, 0)
console.log('bbbb')
方法
// 方法一
for (let i = 0; i< 10; i++){
setTimeout(() => {
console.log(i)
}, 1000)
}
// 方法二
for (var i = 0; i< 10; i++){
((i) => {
setTimeout(() => {
console.log(i)
}, 1000)
})(i)
}
// 方法三
for (var i = 0; i< 10; i++){
// 此时的setTimeout 无效
setTimeout((() => {
console.log(i)
})(), 1000)
}
3、作用域和闭包
var name = 'The Window'
var object = {
name : 'My Object',
getNameFunc: function(){
var that = this
console.log(this.name)
return function(){
console.log(this.name)
console.log(that.name)
}
}
}
object.getNameFunc()()
解析:
闭包有以下三个特性
- 可以访问当前函数以外的变量
- 即使外部函数已经返回,闭包仍能访问外部函数定义的变量
- 闭包可以更新外部变量的值
// My Object
// The Window
// My Object
4、作用域和闭包
var n = 123
let m = 789
function fn () {
var n = 99
console.log(n, m)
console.log(window.n, window.m)
add = function () { n = 100 }
return () => console.log(n)
}
var result = fn()
result()
add()
result()
var demo = fn()
demo()
解析:
99 789
123 undefined
99
100
99 789
123 undefined
99
5、http请求的get和post区别
- get是从服务器上取数据,post是向服务器传数据
- get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制
- get安全性非常低,post安全性较高
拓展
- TCP的三次握手
- http协议的方法
- https通信机制
6、Object.assign 模拟实现
function assign (target) {
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target)
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index]
if (nextSource != null) {
for (var nextKey in nextSource) {
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey]
}
}
}
}
return to
}
拓展
- 函数设计严谨性
- Object强制转换成功Object
- hasOwnProperty的使用 https://www.jianshu.com/p/19764422baa8
7、 模拟实现一个call
- 不传入第一个参数,那么默认为window
- 改变了this指向,让新的对象可以执行该函数。思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除
Function.prototype.myCall = function (context) {
var context = context || window
// 给context添加一个属性
// getValue.call(a, 'cdt', '40') => a.fn = getValue
context.fn = this
// 将context后面的参数取出来
var args = [...arguments].slice(1)
// getValue.call(a, 'cdt', '40') => a.fn('cdt', '40')
var result = context.fn(...args)
// 删除fn
delete context.fn
return result
}
// 执行代码
var val = {
value: 1
}
function getValue(name, age) {
console.log(name)
console.log(age)
console.log(this.value)
}
getValue.myCall(val, 'cdt', '40') // -> cdt 40 1
拓展
-
call 和apply
-
call在严格模式下第一个参数可以为null,无作用域
-
伪数组转数组
// 第一种方法 Array.prototype.slice.call(arrayLike) // 第二种方法 [...arrayLike] // 第三种方法 Array.from(arrayLike)
8、下面代码中 a 在什么情况下会打印 1?
var a = ?
if(a == 1 && a == 2 && a == 3){
console.log(1)
}
答案:
var a = {
i: 1,
toString () {
return a.i++
}
}
解析:
引用类型在比较运算符时候,隐式转换会调用本类型toString
或valueOf
方法。
执行 a == 1时,会调用a这个对象默认值1,并且执行完之后会执行i++,那么a就等于2,以此类推。但是这个判断语句必须按照顺序判断。
拓展
- ++在前,在后
a == 1 && a == 4 && a == 3
这样的情况是不是成功进判断- 隐式转换
toString
https://www.cnblogs.com/chenmeng0818/p/5954215.html
9、给定两个数组,写一个方法来计算它们的交集
var nums1 = [1, 2, 2, 1], nums2 = [2, 2, 3, 4]
// 1. [NaN].indexOf(NaN) === -1,这样子的话 NaN会检测不到
var arr = nums1.filter(item => {
return nums2.indexOf(item) > -1
})
// 2. 这个方法可以
var newArr2 = nums1.filter((item) => {
return nums2.includes(item)
})
10、密码安全
密码安全虽然大多是后端的事情,但是作为一名优秀的前端程序员也需要熟悉这方面的知识。
加盐:
对于密码存储来说,必然是不能明文存储在数据库中的,否则一旦数据库泄露,会对用户造成很大的损失。并且不建议只对密码单纯通过加密算法加密,因为存在彩虹表的关系(彩虹表:理解为用于加密散列逆运算的预先计算好的表)
通过需要对密码加盐,然后进行几次不同加密算法的加密。
// 加盐也就是给原密码添加字符串,增加原密码长度
sha256(sha1(md5(salt + password + salt)))
但是加盐并不能阻止别人盗取账号,只能确保即使数据库泄露,也不会暴露用户的真实密码。一旦攻击者得到了用户的账号,可以通过暴力破解的方式破解密码。对于这种情况,通常使用验证码增加延时或者限制尝试次数的方式。并且一旦用户输入了错误的密码,也不能直接提示用户输错密码,而应该提示账号或密码错误。