Javascript

简介: Javascript interview

变量未定义

console.log(typeof(a))  //undefined
console.log(typeof(typeof(a)))  's'

call / apply

作用:传递参数不同

区别:传参列表不同

区别:call 需要把实参按照形参个数传进去

apply 第二位只能添加一个arguments数组 需要传一个arguments

4undefined 和 null

共同点

  1. 属于基本数据类型
  2. 两个值都是只有一个值 undefined 和 null

不同点

  1. undefined表示属于一个变量定义了 但是没有赋值
  2. null表示是一个空对象 null主要是赋值可能会返回对象的变量 作为初始化

判断数组 / 对象

  1. ES5之前的判断形式

    instanceof

    constructor

    Object.prototype.toString.call([])

  2. ES6判定还是对象

    Array.isArray

var / let / const

  1. 块级作用域 是由{ }包括 let const具有块级作用域 var 不存在块级作用域
    • 块级作用域解决了ES5之前的两个问题

      1. 内层变量会覆盖外层变量
      2. 用来计算的循环变量泄露为全局变量
  2. 存在暂时性死区
    • 暂时性死区

      1. 在使用let、const命令声明变量之前,变量都是不可用的。这在语法上,称为暂时性死区 let a = a/const a = a(会报错
  3. 重复变量声明
    1. var允许重复声明变量
    2. let const 不允许
  4. 必须设置初始值

    • var let 可以不设置初始值
    • cons必须设置初始值
  5. 可以改变指针指向( 重新赋值 )

    • let可以更改指针指向重新赋值
    • const不能更改指针指向 但是可以修改值

箭头函数 / 普通函数

1. 语法简洁 一个参数 可以不写 只有一条语句的时候 不用书写return 内部做的事情等价于return
2. 没有自己的this 延展作用域链去上层查找
3. 没有arguments
4. 不能作为构造函数使用 不能new
5. 没有原型prototype
6. 不能通过call() apply() bind() 改变this指向 在创建的时候 this就已经确定了

解构赋值

数组解构赋值

1. 解构数组时候 是以元素位置 作为匹配条件 取数据

对象的解构赋值

1. 解构对象的时候 是以元素的属性名 作为匹配条件 取数据

判断数组 / 对象

  1. typeof
    • 检测基本数据类型
    • null为object类型
    • function为function类型
  2. instanceof
    • 可以判断对象的类型
    • 可以检测构造函数的原型是否出现在实例对象的原型链上
    • 也就是说函数的原型对象出现实例对象的原型链上就返回true
  3. Object.prototype.toString.call([])
    • 返回的"[object Xxxx]"的字符串

判断数组

  1. xxx instanceof construcotr
    • 返回Boolean
  2. Object.toString.call([])
    • 返回" [obejct Xxxx] " String
  3. [ ].__ proto __ === Array.prototype
    • 原型链的方式
  4. Array.isArray([ ])
    • 通过ES6新方法

判断对象为空对象

  1. JSON.stringify()

    • JSON自带的方法
  2. Object.keys()

    • 使用进行判断
var obj = {}
if(JSON.Stringify(obj) == '{}') console.log('nulll')
if(Object.keys(obj).length <= 0)  console.log('null')

ES6新增

  1. 变量声明
  2. 扩展运行符
  3. 解构赋值
  4. 模板字符串
  5. Promise
  6. await/async
  7. 简化对象写法
  8. 函数形参默认值

const对象的属性更改

  1. const不是他的值不能改变 而是他指向的内存地址 基本数据类型值就是他的内存地址 等于就是常量 引用数据类型 指向的不是值 而是一个指针 const只能保证这个指针不变 里面的解构没办法保证

map / filter

  1. 相同点

    • 都是对数组进行操作 都是返回一个新数组
  2. 不同点

    • filter满足条件留下 对原数组进行过滤 map则是对原数组进行加工 映射成一对一的新数组

Object.assign是深拷贝 / 浅拷贝

  1. 浅拷贝
  2. 如果Object.assign拷贝的只是属性值 假设源对象值的属性值指向对象引用地址 那么Object.assign拷贝的也是引用地址
  3. 总结:

    1. 如果对象属性值为原始值 Number String 通过拷贝的对象为深拷贝
    2. 如果属性值为对象 或 其他引用类型 那么就是浅拷贝

Map 和 Object的区别

  1. 同名碰撞

    1. 对象赋值就是在堆内存中开辟一块内存 Map的键里面就是这块堆内存的内存地址 只要地址不同 就是不同键 解决了同名碰撞问题
  2. 键的类型

    1. Map的键可以是任意类型 String Number function
    2. Object的值只能是String 或者是 Symbol类型
  3. 键的顺序

    1. Map中key是有序的 遍历的时候 是以插入的顺序返回键值对
    2. Object key是无序的
  4. 迭代器 ---- 遍历

    1. Map是生成了迭代器的 可以直接被迭代 可用for of遍历
    2. Object没有迭代器 不能使用for of 遍历
  5. for...in 和 for...of 的区别

    1. 区别
    2. 对象的遍历

      1. for...in会遍历对象的整个原型链 遍历太多没必要的东西 性能非常差 不推荐
      2. for...of无法遍历对象
    3. 数组的遍历

      1. for...in会返回所有数组中的可枚举属性 [包括原型链上的的属性]
      2. for...of只会返回数组对应下标的属性值
const arr = ['hello','liunx','microsoft']
for(var item of arr) {
    console.log(item)
}

遍历数组方法

  1. forEach( )

    1. 视情况而定 是否改变原数组 没有返回值 传入一个回调函数
  2. filter( )

    1. 不改变原数组 返回一个符合筛选条件的新数组
  3. map( )

    1. 不改变元素组 对原数组进行加工 映射成一对一的新数组
  4. find( ) 和 findIndex( )

    1. 不改变原数组
    2. find( )返回的是第一个符合条件的值
    3. findIndex( )返回的是第一个条件的值的索引值
  5. reduce( ) 和 reduceRignt( )

    1. reduce( )对数组正序操作
    2. reduceRight( )对数组逆序操作
  6. for..of( )

    1. 不改变原数组
    2. 返回数组对应下标的属性值

forEach / map

  1. forEach( )

    1. 会对数组每个元素执行一次给定的函数 回调函数接收三个参数 1 当前值 2 当前索引 3 正在操作的对象
    2. 会改变原数组 返回值是undefined
  2. map( )

    1. 不会改变原数组 map是对原数组进行加工 一对一映射成一个新数组

深度clone方法

利用JSON.stringify( )方法 原理就是利用JSON.stringify将对象序列化 在使用JSON.parse反序列化

let obj = {a: 0,b: {c: 0}}
let obj2 = JSON.parse(JSON.stringify(obj))

cookie 字段

Set / Map

  1. Map是键值对,Set是值得集合,当然键和值可以是任何得值
  2. Map可以通过get方法获取值,而set不能因为它只有值
  3. 都能通过迭代器进行for...of 遍历
  4. Set的值是唯一的可以做数组去重,而Map由于没有格式限制,可以做数据存储 复制代码

map / forEach

  1. foreach()方法会针对每一个元素执行提供得函数,该方法没有返回值,是否会改变原数组取决与数组元素的类型是基本类型还是引用类型
  2. map()方法不会改变原数组的值,返回一个新数组,新数组中的值为原数组调用函数处理之后的值: 复制代码

localStorage sessionStorage cookie

  1. localStorage:以键值对的方式存储 储存时间没有限制 永不生效 除非自己删除记录
  2. sessionStorage:当页面关闭后被清理与其他相比不能同源窗口共享 是会话级别的存储方式
  3. cookies 数据不能超过4k 同时因为每次http请求都会携带cookie
  4. 所有cookie只适合保存很小的数据 如会话标识 复制代码

slice / splice / split

// slice(start,[end])
// slice(start,[end])方法:该方法是对数组进行部分截取,该方法返回一个新数组
// 参数start是截取的开始数组索引,end参数等于你要取的最后一个字符的位置值加上1(可选)。
// 包含了源函数从start到 end 所指定的元素,但是不包括end元素,比如a.slice(0,3);
// 如果出现负数就把负数与长度相加后再划分。
// slice中的负数的绝对值若大于数组长度就会显示所有数组
// 若参数只有一个,并且参数大于length,则为空。
// 如果结束位置小于起始位置,则返回空数组
// 返回的个数是end-start的个数
// 不会改变原数组
var arr = [1,2,3,4,5,6]
/*console.log(arr.slice(3))//[4,5,6] 从下标为0的到3,截取3之后的数
console.log(arr.slice(0,3))//[1,2,3] 从下标为0的地方截取到下标为3之前的数
console.log(arr.slice(0,-2))//[1,2,3,4]
console.log(arr.slice(-4,4))//[3,4]
console.log(arr.slice(-7))//[1,2,3,4,5,6]
console.log(arr.slice(-3,-3))// []
console.log(arr.slice(8))//[]*/
// 个人总结:slice的参数如果是正数就从左往右数,如果是负数的话就从右往左边数,
// 截取的数组与数的方向一致,如果是2个参数则截取的是数的交集,没有交集则返回空数组 
// ps:slice也可以切割字符串,用法和数组一样,但要注意空格也算字符

// splice(start,deletecount,item)
// start:起始位置
// deletecount:删除位数
// item:替换的item
// 返回值为被删除的字符串
// 如果有额外的参数,那么item会插入到被移除元素的位置上。
// splice:移除,splice方法从array中移除一个或多个数组,并用新的item替换它们。
//举一个简单的例子 
var a=['a','b','c']; 
var b=a.splice(1,1,'e','f'); 
 console.log(a) //['a', 'e', 'f', 'c']
 console.log(b) //['b']

 var a = [1, 2, 3, 4, 5, 6];
//console.log("被删除的为:",a.splice(1, 1, 8, 9)); //被删除的为:2
// console.log("a数组元素:",a); //1,8,9,3,4,5,6

// console.log("被删除的为:", a.splice(0, 2)); //被删除的为:1,2
// console.log("a数组元素:", a) //3,4,5,6
console.log("被删除的为:", a.splice(1, 0, 2, 2)) //插入 第二个数为0,表示删除0个  
console.log("a数组元素:", a) //1,2,2,2,3,4,5,6

// split(字符串)
// string.split(separator,limit):split方法把这个string分割成片段来创建一个字符串数组。
// 可选参数limit可以限制被分割的片段数量。
// separator参数可以是一个字符串或一个正则表达式。
// 如果separator是一个空字符,会返回一个单字符的数组,不会改变原数组。
var a="0123456";  
var b=a.split("",3);  
console.log(b);//b=["0","1","2"]
// 注意:String.split() 执行的操作与 Array.join 执行的操作是相反的。

类数组转换数组

//通过call调用数组的slice方法来实现转换
Array.prototype.slice.call(arrayLike)

//通过call调用数组的splice方法来实现转换
Array.prototype.splice.call(arrayLike,0)

//通过apply调用数组的concat方法来实现转换
Array.prototype.concat.apply([],arrayLike)

//通过Array.from方法来实现转换
Array.from(arrayLike)

类数组

数组去重

let arr = [1,1,"1","1",true,true,"true",{},{},"{}",null,null,undefined,undefined]

// 方法1
let uniqueOne = Array.from(new Set(arr)) console.log(uniqueOne)

// 方法2
let uniqueTwo = arr => {
    let map = new Map(); //或者用空对象 let obj = {} 利用对象属性不能重复得特性
    let brr = []
    arr.forEach( item => {
        if(!map.has(item)) { //如果是对象得话就判断 !obj[item]
            map.set(item,true) //如果是对象得话就obj[item] =true 其他一样
            brr.push(item)
        }
    })
    return brr
}
console.log(uniqueTwo(arr))

//方法3
let uniqueThree = arr => {
    let brr = []
    arr.forEach(item => {
        // 使用indexOf 返回数组是否包含某个值 没有就返回-1 有就返回下标
        if(brr.indexOf(item) === -1) brr.push(item)
        // 或者使用includes 返回数组是否包含某个值 没有就返回false 有就返回true
        if(!brr.includes(item)) brr.push(item)
    })
    return brr
}
console.log(uniqueThree(arr))

//方法4
let uniqueFour = arr => {                                         
     // 使用 filter 返回符合条件的集合
    let brr = arr.filter((item,index) => {
        return arr.indexOf(item) === index
    })
    return brr
}
console.log(uniqueFour(arr))

数组方法 / 不改变原数组

  1. some( )

    1. arr.some(callback(elements[, index[,array]]))
      接受一个callback(element) callback里面接收三个参数
      element 数组正在处理的元素
      index 数组中正在处理元素的索引值
      返回值
      数组中有至少通过一个元素通过callback测试就会返回true 所有元素都没有通过就callback的测试返回值为false

call / apply / bind

  1. 他们三个共同点

    1. 都是函数的方法
    2. 都是绑定this的值
    3. 都不能被箭头函数调用
  2. call apply 区别就是

    1. apply只接受两个参数 一个 改变this指向的这个对象
    2. 一个数组 数组展开之后会传给原始数组
    3. 如果是call呢? call会一个一个进行传递
    4. call apply直接运行函数了 直接运行结果了
    5. 如果自己实现setTimeout() {}
      function setTimeout(cd,time) {
          
      }
      setTimeout(o.hi,0)

闭包

  1. 什么是闭包?
  2. 用途是什么?

方法 / 函数

作用域 / 作用域链

遍历返回出来对象 / 数组

const obj = ['coder','Deng','okokok']
obj.map(item => ({item})
        //lo [{…}, {…}, {…}]

数组 / 数组对象方法

  • findIndex()*方法返回数组中满足提供的测试函数的第一个元素的*索引。若没有找到对应元素则返回-1。
  • find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined
  • some() 方法测试数组中是不是至少有 1 个元素通过了被提供的函数测试。它返回的是一个 Boolean 类型的值。

    备注: 如果用一个空数组进行测试,在任何情况下它返回的都是 false
  • includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false
  • const arr = [
      {
        name: 'code',id:3489483948394
      },
        {
        name: 'deng',id:489384938943
      },
    ]
    const el = arr.map(item => item.id)
    console.log(el.includes(489384938943))

for / in

  • 在这种情况下可以使用对象的 hasOwnProperty() 方法过滤掉原型链上的属性
  • for (let key in obj) {
     if (obj.hasOwnProperty(key)) {
      console.log(obj[key]) // foo
     }
    }

Object.keys

  • Object.keys() 是 ES5 新增的一个对象方法,该方法返回对象自身属性名组成的数组,它会自动过滤掉原型链上的属性,然后可以通过数组的 forEach() 方法来遍历

for in 循环和 Object.keys() 方法都不会返回对象的不可枚举属性

如果需要遍历不可枚举的属性,就要用到 Object.getOwnPropertyNames() 方法了

Set集合依然保留创建时接受的元素(1、2、3、3),新数组中保存中这些元素的副本,自动移除重复元素不会改变数据源

  1. 用Set集合过滤数组重复元素
function eliminateDuplicates(items) { // 消除重复
    return [...new Set(items)]
}
let numbers = [1, 2, 3, 3, 3, 4, 5] // 定义一个重复元素的数组
console.log(eliminateDuplicates(numbers)) // [ 1, 2, 3, 4, 5 ]

简化后:

let eliminateDuplicates = items => [...new Set(items)]
console.log(eliminateDuplicates(numbers)) // [ 1, 2, 3, 4, 5 ]

这个方法只能过滤重复的普通数组(不包括多维数组),不可以过滤对象数组

类数组是什么

所谓类数组对象,就是指可以通过索引属性访问元素并且拥有 length 属性的对象

arguments

this的绑定规则

注意点:

​ this绑定和编写的位置没有关系

​ this绑定和调用的位置有关系

​ this是在运行时被绑定的

函数执行的时候才开始绑定自己的this

注意点:

​ 他在调用的时候有没有主题

​ 可不是他在定义的时候有没有主题

跟你定义的位置是没有关系的 我管你在哪里定义的

​ 跟我没有关系 跟你定义没有关系 this跟所处的环境是没有关系的

跟你调用的位置有关系

默认绑定

function foo() {
    console.log(this)
}
foo() //window 默认绑定

隐式绑定

object.fn() object对象会被js引擎绑定到fn函数中this里面

function foo() {
    console.log(this)
}
foo() //window
var obj = {
    name: 'coderwhy',
    eat: foo
}
obj.eat() // obj  {name: 'coder', eat: ƒ}

显示绑定

call apply区别

共同点 都是改变this指向
不同点 传参列表不同

bind 返回出来一个新函数

function foo() {
    console.log(this)
}
const newFoo = foo.bind('aaa')
newFo

new绑定四部曲

注意点:

​ 先创建一个空对象

​ 构造函数内部的this指向这个空对象

​ 将构造函数的显示原型赋值给这个对象的隐式原型

​ 将这个对象返回出去

绑定冲突

默认绑定和显示绑定冲突 优先显示显示绑定

内置函数的细节

注意点:

​ 高阶函数都是可以有绑定this的 但是 高阶函数使用this 必须是function(){} 要不然 this无法绑定

forEach() map() find() findIndex()

setTimeout(() => {
    console.log(this)
},3000)
如下
function foo(cd,timeout) {
    cd() // cd是默认绑定 所以this是window
}
foo(function() {},3000)

this绑定优先级

注意点:

new绑定 -> 显示绑定( apply -> call -> bind ) > 隐式绑定( obj.foo( ) ) > 默认绑定( 独立函数调用

splice indexOf

---------------------面试题

typeof判断类型

返回值:

typeof 运算符返回一个字符串,表示操作数的类型。

1 基本数据类型

2 判断是不是function

3 判断是不是对象(在细分就不行了)

4 数组和对象都是 'object类型'

深克隆

四个步骤:

​ 1 判断是不是复杂数据类型,判断为不为空

​ 2 判断是不是数组,不是数组就是对象

​ 3 判断是不是自己身上的属性

​ 4 递归调用

const obj  = {
  name: 'deng',
  age: 18,
  address: {
    age1: {
      hhh: '1111'
    },
    ag2:{
      eee: 'coder'
    }
  }
}
const obj1 = deepClone(obj)
function deeoClone(obj = {}) {
    if (typeof(obj) !== 'object' || obj == null) {
        return obj
    }
    let result
    if (obj instanceof Array) {
        result = []
    } else {
        result = {}
    }
    for (let key in obj) {
        result[key] = deepClone(obj[key])
    }
}

== 和 ===

if 语句和逻辑运算

注意点:

​ truly变量: !!a === true的变量

​ falsely变量: !!a === false的变量

​ 相当于拿到自身的布尔值

记住那五个是false的就行了 0 NaN null undefined ' ' false

闭包

注意点:

​ 函数作为返回值 函数作为参数传递的时候

​ 闭包:自由变量的查找 是在函数定义的地方 向上级作用域查找 不是在执行的地方

函数定义 和 函数 执行 看 渡一教育

实际开发应用场景 隐藏数据 做一个cache工具

this

注意点:

​ this取什么样的值是在函数执行的时候确认的,不是在函数定义的时候确认的

​ this绑定的几种规则

​ 默认绑定

​ 显示绑定

​ new绑定

1 普通函数调用 指向window

2 call apply bind 根据传入的值

3 对象方法中调用 指向当前对象

4 构造函数中this指向实例对象

5 class也是指向的是实例对象本身

6 箭头函数没有this 永远会找上层作用域的this

对象方法中this是当前对象
对象方法中定义setTimeout(function() {this}) 指的是window
对象方法中定义setTimeout(() => {this}) 指的是当前对象 箭头函数没有自己的this

this不同应用场景,如何取值?

手写bind函数

异步和同步

js是单线程语言 解析一行 执行一行

异步不会产生阻塞

同步会产生阻塞

应用场景

网络请求 和 异步任务 在这个过程中 cpu是空闲的 不能浪费资源

如果没有网络请求和定时器 根本就用不到异步

DOM节点的操作

注意点:

​ property形式

​ 是对变量设置一个属性,不会对标签产生影响

​ atttribute

​ 是对标签设置一个属性,对标签进行改变 如下图

实例图

DOM性能

注意点:

​ 1 DOM查询做缓存

​ 2 频繁操作变为一次操作

BOM没什么可考的

事件绑定

注意点:

​ 封装一个通用的事件监听函数

// 事件处理函数
const btn = document.getElementById('btn1')
btn.addEventListener('click',event => {
    console.log('hhhhh')
})
// 封装通用的绑定函数
function bindEvent(el,type,fn) {
    el.addeventListener(type,fn)
}
const a = document.getElementById('link')
btnEvent(a, 'click',e => {
    e.preventDefault() // 阻止默认行为
})

事件冒泡

注意点:

e.stopPropagation() 阻止冒泡

事件冒泡

事件代理

注意点:

​ 代码简洁

​ 减少浏览器内存

​ 一般是在瀑布流 元素众多 难以绑定

​ 不要滥用

手写AJAX

注意点:

xhr.open('请求类型','请求路径', false)

true才是开启异步状态

const xhr = new XMLHttpRequest()
xhr.open('GET','https://baidu.com',true)
xhr.onreadystatechange = function () {
    if (xml.readyState === 4) {
        if (xml.status === 200) {
            console.log(xml.response)
        }
    }
}
xhr.send()

跨域

注意点:

​ 1 同源策略

​ 2 加载图片 css js 可无视同源策略

​ 3 跨域

同源策略

ajax请求时,浏览器要求当前网页和 server端必须同源( 安全 )

同源: 协议 域名 端口 三者必须一致

前端: http://a.com:8080/
server端: https://b.com/api/xxx
协议不同: http / https
域名: a.com / b.com
端口: 8080 / xxx / 8080

加载图片 css js 可无视同源策略

<img /> 可用于统计打点 可使用第三方统计服务’
<link /><script />可使用CDN CDN 一般都是外域
<script> 可实现JSONP

跨域

所有的跨域,都必须经过server端允许

未经server端就实现跨域,说明浏览器有bug

JSONP

注意点:

​ JSONP如何实现跨域?

存储

注意点:

​ 1 cookie

​ 2 localStotage

​ 3 sessionStorage

cookie

1 cookie是http协议的一部分,因为 http是无状态协议,cookie就是为了解决客户端发起请求,无法得知上次请求状态和数据,服务端向客户端发送cookie,服务器向客户端发送cookie,浏览器将cookie保存下来,每次都将cookie保存,每次请求浏览器都会将cookie发送到服务器

2 cookie缺点

存储大小 4kb

请求时发送需要发送到服务端,增加请求数据量

H5存储localStotrage

HTML5专门为存储而设计的 最大保存5M

API简单 setItem getItem

不会在发送请求的时候发送出去

数据永久存储 除非手动删除

sessionStorage

会话级存储

只存在于会话期间,浏览器关闭则清空

三者区别

容量

API易用性

是否发送请求的时候发送出去

一般localStorage用的多

运行环境性能优化

注意点:

window.onload和DOMContentLoaded区别

Ajax / Feath / Axios

注意点:

​ 三者都用于网络请求

Ajax

技术的统称 一般能实现网络请求的,都叫Ajax

Feath

一个原声API Function 支持Promise

Axios

一个封装好的http第三方库

库 / API

api就是别人已经实现的,直接用就行

库使用api实现的,用户可以自己实现库

相关文章
|
12月前
|
JavaScript 前端开发
70.【JavaScript 6.0】(五)
70.【JavaScript 6.0】
63 1
|
12月前
|
存储 JSON JavaScript
70.【JavaScript 6.0】(四)
70.【JavaScript 6.0】
45 1
|
XML JavaScript 前端开发
javascript之webAPIs(1)
javascript之webAPIs(1)
60 0
|
4月前
|
JavaScript 前端开发
JavaScript-T2
JavaScript-T2
24 0
|
4月前
|
存储 JavaScript 前端开发
Javascript
avaScript 是一种用于在网页上实现交互性和动态功能的脚本语言。
46 0
|
JavaScript 前端开发
JavaScript 能够做什么?
JavaScript 能够做什么?
81 0
|
JavaScript 前端开发
JavaScript中的this
JavaScript中的this
JavaScript中的this
|
编解码 JavaScript 前端开发
初识JavaScript
初识JavaScript
216 0
初识JavaScript
|
JSON JavaScript 前端开发
你不知道的JavaScript丛书总结(二)
你不知道的JavaScript丛书总结(二)
|
自然语言处理 JavaScript 前端开发
JavaScript之扑朔迷离的this
JavaScript之扑朔迷离的this JavaScript这门语言中,最令人迷惑的地方有三个,闭包、this、原型。针对大多数人,可以利用词法作用域等避开this的坑,但是我们不能一直生活在舒适区,要敢于打破砂锅问到底,对我们来说也是一种提升。
1005 0