【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();

代码执行逻辑

  1. 同步执行完成后执行异步程序。
  2. 一个同步执行完成后才能执行另一个同步程序。(js是单线程的)
  3. 计时器只是到点了放入任务队列里面,事件循环中调用还要看前面的任务,所以计时器是“不准”的。

代码执行顺序:

  1. 同步代码
  2. process.nextTick // 只有node内有
  3. 异步代码
    1. 异步宏任务:ajax、读取文件、定时器
    2. 异步微任务:promise.then (promise内的是同步代码,promise.then内的才是异步微任务代码
  4. 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)
})

但是这样会有两个问题:

  1. 有个全局变量timer。
  2. 防抖代码和业务逻辑代码集成到一起了。

解决:

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关键字来继承。
posted @ 2022-03-23 15:13  小默同学  阅读(32)  评论(0编辑  收藏  举报