1、call、apply区别
相同点:都是重定向this指针的方法。
不同点:call和apply的第二个参数不相同,call是若干个参数的列表。apply是一个数组
手写一个call方法
// 在这之前需要重新认识一下call方法的执行操作
let mock = { value : 1 };
function mockNum(){
console.log('value',this.value)
}
mockNum.call(mock) // 改变了函数中this的指向,当前this指向了mock对象
// 转换一下实现方法就是
let mock = {
value:1;
mockNum:function(){
console.log('value',this.value)
}
}
mock.mockNum();
// 所以经过上面这个操作的演化而来的结果就是如下步骤:
// 1. 将函数设为一个对象的属性
// 2. 并将这个函数的属性调用
// 3. 删除该函数
Function.prototype.Mycall = function(context){
let obj = context || window;
obj.fn = this; // 这一步可以看做是this其实就指的当前函数。
let args = [...arguments].slice(1); // 返回删除第一个元素的数组;
let result = obj.fn(...args); // 调用函数
delete obj.fn;
return result;
}
// 操作一下
let mock = { value : 1 };
function mockNum(){
console.log('value',this.value);
}
mockNum.Mycall(mock) // value 1
2、bind
bind方法是直接返回一个新的函数,需要手动去调用才能执行。
手写一个bind方法
// 例如:
let foo = { value : 1 };
function bar() {
console.log('bindFoo',this.value);
// return this.value // 考虑到函数可能有返回值
}
let bindFoo = bar.bind(foo);
bindFoo() // 1 // 如果有返回值的情况下 bindFoo() === 1;
Function.prototype.Mybind = function(obj){
if(typeof this !== 'function') throw new Error('not a function');
let self = this;
let args = [...arguments].clice(1);
return function F(){
if(this instanceof F){
return new self(...args,...arguments);
}
return self.apply(obj,args.concat([...arguments]));
}
}
3、讲一下let、var、const的区别
- var 没有块级作用域,支持变量提升。
- let 有块级作用域,不支持变量提升。不允许重复声明,暂存性死区。不能通过window.变量名进行访问.
- const 有块级作用域,不支持变量提升,不允许重复声明,暂存性死区。声明一个变量一旦声明就不能改变,改变报错。
4、手写函数防抖与节流
防抖
function debounce(fn, wait, immediate) {
let timer = null;
// 返回一个函数
return function(...args) {
// 每次触发事件时都取消之前的定时器
clearTimeout(timer);
// 判断是否要立即执行一次
if(immediate && !timer) {
fn.apply(this, args);
}
// setTimeout中使用箭头函数,就是让 this指向 返回的该闭包函数,而不是 debounce函数的调用者
timer = setTimeout(() => {
fn.apply(this, args)
}, wait)
}
}
节流
function throttle(fn, wait) {
// 设置一个定时器
let timer = null;
return function(...args) {
// 判断如果定时器不存在就执行,存在则不执行
if(!timer) {
// 设置下一个定时器
timer = setTimeout(() => {
// 然后执行函数,清空定时器
timer = null;
fn.apply(this, args)
}, wait)
}
}
}
5、js跨域如何解决
目前暂时已知的跨域方法是:
- jsonp跨域,原理:script标签没有跨域限制的漏洞实现的一种跨域方法,只支持get请求。安全问题会受到威胁。
- cors跨域,通过后端服务器实现,Access-Control-Allow-Origin。
6、script标签如何实现异步加载
- defer:等到整个页面在内存中华正常渲染结束(DOM结构完全生成,以及其他脚本执行完成),才会执行;
- async是一旦下载完成,渲染就会中断,执行这个脚本之后,再继续渲染。
总结就是:defer是渲染完在执行。async是下载完就执行。
另外值得注意的就是:deger脚本会按照在页面出现的顺序加载,而async是不能保证加载顺序的。
7、手写js深拷贝
export const deepClone = data => {
var type = getObjType(data)
var obj
if (type === 'array') {
obj = []
} else if (type === 'object') {
obj = {}
} else {
// 不再具有下一层次
return data
}
if (type === 'array') {
for (var i = 0, len = data.length; i < len; i++) {
obj.push(deepClone(data[i]))
}
} else if (type === 'object') {
for (var key in data) {
obj[key] = deepClone(data[key])
}
}
return obj
}
持续更新中,敬请期待…