开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情
1.谈谈对this的理解
this是一个关键字,是函数在运行时自动生成的一个内部对象,只能在函数内部使用。
- 给DOM元素进行事件绑定,当事件行为触发,绑定的方法执行,方法的this就是DOM元素本身
- 当方法执行,我们看函数前面是否有“点”
- 有“点”,点前面就是this指向
- 没有,this就是“window”,“undefined”,非严格模式和严格模式的区别。注意node中全局对象指向global。
- 匿名函数,自执行函数,回调函数,this一般是window/undefined
2.js内置对象
Object是js中所有对象的父对象。
数据封装类对象:Object,Array,Number,String,Bollen
其他对象:Function,Date,Math,RegExp,Error
3.闭包
在内层函数访问外层作用域。
使用场景:1.创建私有变量。2.延长变量的私有周期。
缺点:容易造成内存泄漏。内存泄漏是指不再用到的内存,没有及时的释放。
应用场景:
4.垃圾回收
垃圾回收(GC):收回不使用的内存 方法:
- 引用计数法:跟踪记录每个对象被引用的次数,被引用+1,引用结束-1,当引用次数为0时回收变量。但是当两个变量之间相互引用时,会造成内存泄漏。
- 标记清除法:从根部出发,清除无法到达的对象。
5.作用域和作用域链
作用域分为全局作用域和局部作用域。
全局作用域:定义的变量和函数,可以在程序的任意地方访问地到。
局部作用域:分为块级作用域和函数作用域,定义的变量或者函数,都只能在它们内部访问。 PS:局部变量会被垃圾回收干掉,所以想延长变量的私有周期,可以使用闭包。
作用域链:寻找变量的过程。
6.JS事件执行顺序
顺序:捕获=》执行=》冒泡
true是捕获,false是冒泡,默认是false。
7.原型和原型链
原型:js也是一种基于原型的语言,每个对象拥有一个原型对象。
原型链:寻找公共属性的机制。
当访问一个对象的属性时,如果没有,还会搜索对象的原型及对象原型的原型,依次层层向上搜索,直到找到或者Object.prototype.proto=null.这就是原型链。
原型链的继承:父级函数的实例化对象绑定子级的原型对象。
8.new的过程
- 创建新对象
- 更改新对象的__proto__
- 更改this指向,并执行构造函数
- 返回这个新对象
function myNew(fn,...args) {
const obj={}
obj.__proto__=fn.prototype
fn.apply(obj.args)
return obj
}
9.更改this指向的三个方法
- call
第一个参数是this指向,后面是实参且个数不受限制。可以调用函数。
- apply
第一个参数是this指向,第二个是数组。可以调用函数。
- bind
参数跟call一样,但是使用bind不会调用函数。有一个返回值,返回this的指向。
10.浅拷贝和深拷贝
JS有两大类型:基本类型和引用类型。
基本类型:
- Boolean
- Number
- String
- null
- undefined
- symbol
引用类型:
- Object
- Array
- Function
- Set
基本类型的数据保存在栈中;引用类型的数据保存在堆中,变量是一个指向堆内存的引用且存在栈中。
浅拷贝
如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址。
也就是说,修改一个对象的属性,另一个也发生改变。
三种方式:
- 使用Object.assign(),将右边的键值加到左边。
- 使用forin循环添加。
- 直接赋值添加。
const obj = { name: '浙江', address: '中国' }
const o = { age: 20 }
// 第一种,把右边的键值对加到左边
Object.assign(o, obj)
// 第二种
for (let key in obj) {
o[key] = obj[key]
}
// 第三种
o.name = obj.name
o.address = obj.address
console.log(o);
深拷贝
当对象是引用类型时,开辟一个新栈,两个对象的属性完全相同,但是对应两个完全不同的地址。
修改一个对象的属性,另外一个不会发生改变。
方式:
- 工作的项目里使用lodash的_cloneDeep()
- JSON.stringfy()。注意不可以拷贝函数(引用类型Function),undefined,symbol。
- jq里面的extend()
- 通过递归来实现
const obj2=JSON.parse(JSON.stringify(obj1));
11.抛出异常
12.web常见的攻击方式
- SQL注入
恶意的将sql语句插入到表单中,让程序在后台解析。
- CSRF
跨站请求伪造:诱导用户登录一个虚假的第三方网站,获取用户的Cookie后,携带Cookie向第三方网站发送跨站请求。
- XSS
跨站脚本攻击:允许攻击者将恶意代码植入到提供给其他用户使用的页面中。
12.如何实现上拉加载
上拉加载本质是页面触底,或者快要触底的动作。
判断页面触底,要先了解几个属性:
- scrolltop:滚动视窗距离window顶部的距离。
- clientHeight:定值,表示屏幕可视区域的高度。
- scrollHeight:页面不能滚动时也是存在的,此时scrollHeight等于clientHeight。scrollHeight表示
body
所有元素的总长度(包括body元素自身的padding)。
let clientHeight = document.documentElement.clientHeight; //浏览器高度
let scrollHeight = document.body.scrollHeight;
let scrollTop = document.documentElement.scrollTop;
let distance = 50; //距离视窗还用50的时候,开始触发;
if ((scrollTop + clientHeight) >= (scrollHeight - distance)) {
console.log("开始加载数据");
}
// 拉到底部后,刷新即可在控制台看到数据!
注意这里的scrollHeight与offsetHeight可以替换,也是能达到相同的效果
13.下拉刷新
页面本身置于顶部时,用户下拉时触发的动作。
三个阶段:
- 当前手势滑动位置与初始位置的差值大于0的时候,提示正在进行下拉操作。((在scrollTop为0的时候进行判断)
- 下拉到一定值时,提示松手后的操作。
- 松手,执行更新回调。
14.大文件如何实现断点上传
拿到文件,保存文件的唯一标识,切割文件,逐一上传,同时根据唯一标识同步上传进度。直到文件全部上传完成。
15.防抖与节流
防抖:当用户频繁触发时,前面所有触发都取消,到达规定时间,执行最后一次触发
节流:当用户频繁触发时,不根据用户频繁程度来触发,而是根据设定好的频率来触发。可以触发多次
采用lodash防抖
16解决js数字精度丢失的问题
为什么0.1+0.2不等于0.3?
因为计算机存储浮点数时,要把十进制转化为二进制,由于存储位数有限(64位),所以某些数字在十进制转化为二进制时会出现精度丢失问题。
解决方案:使用toFixed,toPrecision 凑整并 parseFloat,第三方js库(例如bigInt)。
console.log(Number((0.1+0.2).toFixed(1))===0.3)// true
17事件循环
同步任务在执行栈执行完之后,会去异步队列查看是否有待执行的异步任务的回调函数,有就拿到执行栈执行,执行完之后又去异步队列查看。这个反复查看的过程就是事件循环(eventloop)
18.ES6新增了什么?
-
新增声明命令let,const
-
模板字符串:``
-
函数扩展
- 默认参数
- 箭头函数
-
对象扩展
- 属性简写:对象的键值名相同,可以只写一个。
- 方法简写:省略冒号和function关键字。
- Object.keys()/Object.values():获取对象的所有键/值,返回一个数组。
- Object.assign():浅拷贝一个对象。
-
for of 循环
-
import、export。模块化的导入导出
-
Promise
-
解构赋值
-
Set数据结构
-
展开运算符
所有数据是唯一的,都是值。且Set()本身是一个构造函数
19.caller和callee
caller 直接翻译为调用者,callee 翻译为被召者。
caller
返回一个对函数的引用,该函数调用了当前函数。
var a = function () {
console.log(a.caller);
}
var b = function () {
a();
}
b(); // [Function: b] 函数b里面调用了函数a
a(); // null
callee
var a = function () {
console.log(arguments.callee);
// [Function: a]
}
var b = function () {
a();
}
b();
20.强制类型转换
- 转化成字符串toString(),String()
- 转化成数字Number(),parseInt(),parseFloat()
- 转换成布尔类型Boolean()
21.typeof和instanceof的区别
typeof:可以准确的判断简单类型数据的类型,但是判断复杂数据类型时,除了Function都返回“object”。
instanceof就可以解决这个问题。
const a=function name() {}
const b=[1,2]
const c={}
console.log(typeof(a)) // function
console.log(typeof(b)) // object
console.log(typeof(c)) // object
console.log(b instanceof Array) // true
console.log(c instanceof Array) // false
console.log(c instanceof Object) // true
22.什么是回调函数?
函数A被当作实参传入函数B中,并在函数B中被调用,(用来完成某种任务),那么函数A被称为回调函数。
23.forin和forof的区别
ES5出来的forin循环主要是用来遍历对象:
const obj={name:'张三',gender:'male',age:30}
for(let key in obj){
console.log(`这是key:${key}`)
console.log(`这是value:${obj[key]}`)
}
/*
这是key:name
这是value:张三
这是key:gender
这是value:male
这是key:age
这是value:30
*/
不用forin来遍历数组是因为会存在一些问题,比如索引index类型不是number而是string:
const arr=[1,2,3]
for(let index in arr){
console.log(index+1)
}
/*
01
11
21
*/
所以ES6出了forof循环,专门来解决forin不便于遍历数组的问题。举例:
const arr=[1,2,3]
for(let value of arr){
console.log(value)
}
/*
1
2
3
*/
24.split与join的区别
split拆开,join合并。括号里可以写操作目标符号。
const str='arrerr'
const newArr=str.split('')
console.log(newArr) // [ 'a', 'r', 'r', 'e', 'r', 'r' ]
const newStr=newArr.join('')
console.log(newStr) // arrerr
25.数组去重
indexof
/**
* 数组去重:
* 使用indexOf方法,查看当前元素第一次在数组中出现的下标,
* 如果和当前元素的下标不同,那么就表明该元素有重复,就去掉当前元素。
*/
const arr=[1,2,3,4,4,6,7,6]
const newarr=[] // [ 1, 2, 3, 4, 6, 7 ]
for(let i=0;i<arr.length;i++){
if(arr.indexOf(arr[i])===i) newarr.push(arr[i])
}
console.log(newarr)
// !相同思路,使用filter
// let newarr = arr.filter((item,index,self)=>{
// return self.indexOf(item)==index
// })
// console.log(newarr)
Set
const arr = [1, 2, 3, 3, 5, 5]
console.log(new Set(arr)) // Set(4) { 1, 2, 3, 5 } 是set结构,非伪对象
console.log(Array.from(new Set(arr))) // [ 1, 2, 3, 5 ]
26终止/跳过for循环的三种方式
- return
- continue
- trycatch(这个也是唯一终止foreach的方式)
for(let i=0;i<100;i++){
// return 循环终止
// if(i==10)return
// 循环跳过10
if(i==10){
continue
}
console.log(i)
}
// 使用trycatch,这个也是唯一终止foreach的方式