【JavaScript面试题】2020/3/23
new关键字的作用
创建一个对象并且将构造函数内的this指针指向该对象。代码如下:
function Func() { // 构造函数名字首字母一般大写
this.name = "nerd";
this.age = 18;
}
let handsome_guy = new Func();
console.log(handsome_guy.name);
this在普通函数和在箭头函数的区别
普通函数如果定义在全局的函数内(包括定时器的回调函数或者自定义函数),this会指向window;定义在对象的方法内会指向对象,定义在事件的回调函数内会指向该事件节点。而箭头函数中没有this指针,箭头函数定义在哪,this就指向哪。
let obj = {
name: "nerd",
age : 18,
say() {
console.log(this); // obj
setTimeout(function (){
console.log(this); // window
},1000);
setTimeout(()=>{
console.log(this); // obj
});
}
}
obj.say();
call、apply和bind
call可以用来调用函数,来改变函数内this指针的指向,第一个参数是this的指向的对象,后面的参数都是函数的参数:
function Person(name) {
this.name = name;
console.log("My name is "+name + ",My age is "+ this.age);
}
Person.call({age:18},"nerd");
call会调用函数一次,apply和bind的用法和call差不多,apply是传参的时候传入的是一个数组,bind是不会调用函数会返回一个函数:
Person.apply({age:18}, ["nerd",]);
let func = Person.bind({age:18}, "nerd");
call的应用:call可以用来对构造函数进行多继承,代码如下:
function Animal() {
this.name = name;
this.eat = function() {
console.log("我喜欢吃东西~");
}
}
function SuperHero() {
this.do = function() {
console.log("我来拯救地球!");
}
}
function Cat() {
Animal.call(this); // 多继承
SuperHero.call(this);
}
let cat = new Cat();
cat.eat();
cat.do();
代码执行逻辑
- 同步执行完成后执行异步程序。
- 一个同步执行完成后才能执行另一个同步程序。(js是单线程的)
- 计时器只是到点了放入任务队列里面,事件循环中调用还要看前面的任务,所以计时器是“不准”的。
代码执行顺序:
- 同步代码
- process.nextTick // 只有node内有
- 异步代码
- 异步宏任务:ajax、读取文件、定时器
- 异步微任务:promise.then (promise内的是同步代码,promise.then内的才是异步微任务代码)
- setImmediate
promise和async
async函数会把返回值封装成一个promise对象,要获得这个函数的返回值要调用then方法:
async function func() {
return 111;
}
let obj = func();
obj.then(result=>console.log(result));
为了避免使用回调函数,可以用await把异步代码写成同步代码:
let a = new Promise(reslove=>resolve(111));
let b = new Promise(reslove=>resolve(222));
async function func() {
let aa = await aa; // aa=111
let bb = await bb; // bb=222
console.log(aa, bb);
}
这样写让我有一个想法,因为axios是promise对象,于是,可以再vue里面把这个异步ajax请求写的更装逼一点:
methods : {
async ClickMe() {
let result = await axios.get(url);
console.log(result.data);
}
}
Object.getOwnPropertyDescriptor 和 Object.defineProperty
使用如下:
// 有个对象叫student,里面有个name属性
const val = Object.getOwnPropertyDescriptor(student, "name");
// 输出:{ value: 'nerd', writable: true, enumerable: true, configurable: true }
// value:值;writable:是否可以修改;enumerable:是否可以遍历到;configurable:是否可以删除
// 设置属性:
Object.defineProperty(student, "age", {value:18,writable:true,enumerate:true, configurable:true});
闭包
函数执行完后内部变量会被销毁,闭包执行完后,内部变量未被执行完外部函数的变量就不会被销毁。
深拷贝
用JSON进行深拷贝:
let str = JSON.stringfy(obj);
let obj = JSON.parse(str);
防抖
防止输入频率过高,多次给服务器发送请求。代码如下:
let timer;
document.querySelector("input").addEventListener('input', function(event) {
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(()=>{
console.log(this.value);
}, 1000)
})
但是这样会有两个问题:
- 有个全局变量timer。
- 防抖代码和业务逻辑代码集成到一起了。
解决:
document.querySelector("input").addEventListener('input',debounce(function() {
console.log(this.value); // 业务逻辑在回调函数里面写
}, 1000));
function debounce(func, delay) {
let timer;
return function() {
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(()=>{
func.call(this); // 改变this指针,不然回调函数里面指针就是window了
}, delay)
}
}
节流
防抖是只执行最后一次,节流是控制执行次数。代码如下:
document.addEventListener('scroll', throttle(function () {
console.log(111);
}, 500))
function throttle(func, delay) {
let flag = true;
return function () {
if(flag) {
setTimeout(function () {
func.call(this);
flag = true;
}, delay)
}
flag = false;
}
}
原型与原型链
获得原型:
对象.__proto__
构造函数或类.prototype
继承:
- es5是以原型链赋值来继承。
- es6是以extends关键字来继承。