1.闭包:内部函数引用外部函数的变量(高频)
优点:局部变量:仅函数内可用不会被污染
function a(){
var n = 0;
function add(){
n++;
console.log(n);
}
return add;
}
var a1 = a(); //注意,函数名只是一个标识(指向函数的指针),而()才是执行函数;
a1(); //1
a1(); //2 第二次调用n变量还在内存中
var f=(function fn(){
var name=1;
return function(){
name++;
console.log(name);
}
})();
function init(){
var name="zhangsan"; // 创建局部变量name和局部函数alertName
function alertName(){ //alertName()是函数内部方法,是一个闭包
console.log(name) //使用了外部函数声明的变量,内部函数可以访问外部函数的变量
}
alertName()
}
init()
函数内部嵌套了一个新的函数,嵌套的函数对外部的函数造成了引用就形成了闭包???
2.JavaScript this、闭包、作用域(高频)
3.eval是做什么的?
eval 的功能是把对应的字符串解析成 JS 代码并运行
示例:
语法:eval(string)
输出
200
4
27
4.谈谈This对象的理解。
this的五种情况:
1.直接调用函数指向window
function MyObject(){
console.log(this);
}
MyObject();//window
2.对象函数调用,用对象点的情况,指向这个对象
var test = {
a:0,
b: {
c:7,
fn:function(){
alert(this.c);//7
}
}
};
window.test.b.fn();
3.new对象时候this指向这个对象
function f2(){
this.x = 1;
return this;
}
var a = new f2();
console.log(a.x);//1
4.箭头函数指向上下文
全局范围:指向全局对象(浏览器下指window)
全局函数:指向全局对象。
对象函数:调用某个对象的函数,指向当前对象。
new实例化对象时:指向新创建的对象。
JavaScript 秘密花园
https://www.zhihu.com/question/19636194/answer/25081397
5.JS的作用域链是什么及其作用
一般情况下,变量取值到创建这个变量的函数的作用域中取值。但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。
JS中的作用域链主要用于解析变量的值。 如果没有这个,在不同的作用域内定义了许多变量,JS很难为变量选择某个值。
6.函数(function)和方法(method)(高频)
区别:
本质上是一样的,方法是函数的特例,将函数赋值给了对象
函数:可以实现一定功能的一段代码的封装
function f() {
console.log('我是函数'); //调用函数 f();
}
方法:把函数定义到对象里面就称为方法
var obj = {
function f(){
console.log("我是方法");
}
};
//对象调用方法 (对象调用通过点对象的属性名) obj.f();
7.简述创建函数的几种方式(高频)
// 第一种字面量(函数声明):
语法:关键字 标识符 小括号 {函数体}
function sum1(num1,num2){
return num1+num2;
}
// 第二种 表达式(匿名函数表达式):
语法: 关键字 标识符 = function() 函数体
var sum2 = function(num1,num2){
return num1+num2;
}
const square = function(number) { return number * number; };
var x = square(4); // x gets the value 16
// 注意:匿名函数:没有函数名称,无法通过函数名称调用。function(){}:只能自己执行自己
// 第三种(Function构造函数方式):
语法: 关键字 标识符 = new Function()
var sum3 = new Function("num1","num2","return num1+num2");
注:内存栈:sum3:函数的引用
内存堆:"num1","num2","return num1+num2",就是一个字符串
注意:函数提升仅适用于函数声明,而不适用于函数表达式。
函数的三要素 :
// 把函数当作参数传递给另一个函数
function map(f,a){
let result = [];
for(let i = 0; i !== a.length; i++){
result[i] = f(a[i]);
}
return result;
}
const f = funtion(x){
return x*x*x;
}
let numbers = [0,1,2,5,10];
let cube = map(f,numbers);
console.log(cube); // [0, 1, 8, 125, 1000]
Function 构造函数
Function 构造函数与函数声明之间的不同
// 由 Function 构造函数创建的函数不会创建当前环境的闭包,它们总是被创建于全局环境,因此在运行时它们只能访问全局变量和自己的局部变量,不能访问它们被 Function 构造函数创建时所在的作用域的变量。这一点与使用 eval() 执行创建函数的代码不同。
var x = 10;
function createFunction1() {
var x = 20;
return new Function('return x;'); // 这里的 x 指向最上面全局作用域内的 x
}
function createFunction2() {
var x = 20;
function f() {
return x; // 这里的 x 指向上方本地作用域内的 x
}
return f;
}
var f1 = createFunction1();
console.log(f1()); // 10
var f2 = createFunction2();
console.log(f2()); // 20
8.什么是函数的封装?
高内聚,低耦合.
9.函数的执行过程(重要)
在函数执行时,函数体中可以找到函数外面的变量;但是函数外面不能访问里面的变量;
10.构造函数与普通函数的区别
// 定义构造函数
function Show(){
this.name = "张三"
this.age = "12"
}
var show = new Show() // 调用构造函数
alert(show.name) // 张三
// 普通函数
function show (name,age){
this.name = 'zhangsan';
this.age = 12;
}
alert(show(this.name)) //zhangsanm
12.介绍箭头函数和普通函数的区别
箭头函数与普通函数的区别详解_小弦的博客的博客-CSDN博客_箭头函数与普通函数有哪些区别
相同:箭头函数和普通函数,都是封装了多行代码实现了一定功能的代码块,都可以提高代码的复用性
不同点:
箭头函数比普通函数更加简洁 如果没有参数,就直接写一个空括号即可 如果只有一个参数,可以省去参数括号 如果有多个参数,用逗号分割 如果函数体的返回值只有一句,可以省略大括号 如果函数体不需要返回值,且只有一句话,可以给这个语句前面加一个void关键字。最常用的就是调用一个函数: let fn = () => void doesNotReturn()
箭头函数没有自己的this 箭头函数不会创建自己的this,所以它没有自己的this,它只会在自己作用域的上一层继承this。所以箭头函数中的this的指向在它在定义时一家确定了,之后不会改变。
箭头函数继承来的this指向永远不会改变
call()、apply()、bind()等方法不能改变箭头函数中的this指向
箭头函数不能作为构造函数使用
箭头函数没有自己的arguments
箭头函数没有prototype
箭头函数不能用作Generator函数,不能使用yeild关键字
注意:关于箭头函数内的 this ,某些资料会提到箭头函数没有 this 指
向(如果真的没有 this 指向,内部无法访问 this 引用);但是如果箭头函
数包含在其他有 this 指向的函数内部,箭头函数内的 this 就是外部函数的
this
练习题
13.new 对象时发生了什么?
function fakeNew() {
// 创建新对象
var obj = Object.create(null);
var Constructor = [].shift.call(arguments);
// 将对象的 __proto__ 赋值为构造函数的 prototype 属性
obj.__proto__ = Constructor.prototype;
// 将构造函数内部的 this 赋值为新对象
var ret = Constructor.apply(obj, arguments);
// 返回新对象
return typeof ret === "object" && ret !== null ? ret : obj;
}
function Group(name, member) {
this.name = name;
this.member = member;
}
var group = fakeNew(Group, "hzfe", 17);
完全搞懂js 中的new()到底做了什么?_张木期的博客-CSDN博客_js new
1、创建一个空对象、并且this变量引用该函数、同时还继承了该函数的原型
2、属性和方法被添加到this引入的对象中
3、新创建的对象由this说引用、并且最后隐式的返回this
var obj = {};
obj.__proto__ = Base.prototype;
Base.call(obj);
14.创建对象的方式有几种?(高频)
// 1.对象字面量(最常用的方式)
// 对象字面量创建了一个对象o1
var o1 = {
p:"hello world",
alertP:function(){
alert(this.p);
}
}
缺点 :每创建一个新的对象都需要写出完整的定义语句,不便于创建大量相同类型的对象,不利于使用继承等高级特性。
// 2.用new构造对象
function CO(){
this.p = “I’m in constructed object”;
this.alertP = function(){
alert(this.p);
}
}
var o2 = new CO();
深入理解js构造函数 - 奔跑的蜗牛~ - 博客园
15.call,apply,bind有什么作用?区别是什么?(高频)
this指向有哪些? call,apply,bind
call和apply
建议使用es6的箭头函数:这样就解决了this的指向问题
// 无参数调用:
function fn(){
alert(this.name);
}
var p1={name:1};
fn.call(p1);
fn.apply(p1);
// 有参数调用:
function fn2(name,age){
this.name=name;
this.age=age;
}
var p1={};
fn2.call(p1,"张三",20);
fn2.apply(p1,["张三",20]);
16.形参(parameter)和实参(argument)的区别(高频)
//形参就是函数声明时的变量
//实参就是函数随时参入的具体参数
// a,b是形参,1,3是实参
function add(a,b){
return a+ b
}
add(1,3)
17.事件绑定和普通事件有什么区别
普通事件(onclick):直接触发事件,同一时间只能指向唯一对象,所以会被覆盖掉
var btn = document.getElementById("btn")
btn.onclick = function(){
alter("你好111")
}
btn.onclick = function(){
alter("你好222")
}
// 输出的结果只会有<你好222>,一个click处理器在同一时间
只能指向唯一的对象。所以就算一个对象绑定了多次,其结果只
会出现最后的一次绑定的对象。
事件绑定(addEventListener):事件绑定就是对于一个可以绑定的事件对象,进行多次绑定事件都能运行
var btn = document.getElementById("btn");
btn.addEventListener("click",function(){
alert("你好111");
},false);
btn.addEventListener("click",function(){
alert("你好222");
},false);
//运行结果会依次弹出你好111,你好222的弹出框。
区别:
18.对内存泄漏的了解
定义:程序中已在堆中分配的内存,因为某种原因未释放或者无法释放的问题
简单理解: 无用的内存还在占用,得不到释放和归还,比较严重的时候,无用的内存还会增加,从而导致整个系统卡顿,甚至崩溃
19.哪些操作会造成内存泄漏?
setInterval
定时器,忘记取消它,如果循环函数有对外部变量的引用的话,这个变量会被一直留在内存中,而无法被回收)20.设置定时器: setTimeout(cb,ms) ; setInterval(cb,ms)
相同点:全局函数在指定的毫秒(ms)内执行指定函数(cb)
区别:
setTimeout(cb,ms) :只执行一次指定函数
function printHello(){
console.log( "Hello, World!");
}
// 两秒后执行以上函数
var timer = setTimeout(printHello, 2000);
//清除定时器
clearTimeout(timer)
setInterval(cb,ms)方法会不停的调用,知道clearInterval()被调用或关闭窗口
function printHello(){
console.log( "Hello, World!");
}
// 两秒后执行以上函数
var timer = setInterval(printHello, 2000);
// 清除定时器
clearInterval(timer);
21.清除定时器:clearTimeout() ,clearInterval()
22.js延迟加载的方式有哪些?
js 的加载、解析和执行会阻塞页面的渲染过程,我们希望 js 脚本能尽可能的延迟加载,提高页面的渲染速度
23.ajax请求时,如何解析json数据:json.parse
24.ajax同步,异步的区别:同步async:false;异步async:true
25.请解释同步和异步函数的区别(高频),怎么解决异步问题(高频)
同步:如果在同一时间内有很多任务的话,这些任务需要排队,一个一个的执行,前一个任务执行完,才会执行下一个任务.
// 同步代码
function fun1() {
console.log(1);
}
function fun2() {
console.log(2);
}
fun1();
fun2();
// 输出
1
2
异步:如果在同一时间内有很多任务的话,这些任务不需要排队就可以去完成.任务都完成就结束了(我昨天是不是就是这样说的?公交车那个)
同步:浏览器访问服务器请求,用户看得到页面刷新,重新发请求,等请求完,页面刷新,新内容出现,用户看到新内容,进行下一步操作
异步:浏览器访问服务器请求,用户正常操作,浏览器后端进行请求。等请求完,页面不刷新,新内容也会出现,用户看到新内容
26.JS如何实现异步编程5种?
优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。)
缺点:回调地狱,每个任务只能指定一个回调函数,不能 return.
27.对async、await的理解,内部原理
async await 用来解决异步的。async函数是Generator函数的语法糖
async表示函数里面有异步操作,await 这个函数必须得用async修饰,await是等后面的异步完成,等待返回结果,async修饰的函数默认返回promise,其实是.then链式调用的同步写法
// async
async function test(){
return 'test'
}
test()
返回值为 Promise {: "test"}
// await
function getSomething(){
return 'something'
}
async function testAsync(){
return Promise.resolve('hello async')
}
async function test(){
const v1 = await getSomething();
const v2 = await testAsync()
console.log(v1,v2)
}
test()
// 控制台输入结果
something hello async
Promise {: undefined}
async/await 怎么抛出错误异常 ?
出错代码少可以用try/catch来处理,出错代码多用async函数返回一个promise对象来处理,调用catch方法来处理异常
async/await 使用,异步过程同步化?
async函数返回的是一个Promise对象,函数有返回值,调用这个函数,如果执行成功,内部会调用Promise.solve()方法返回一个Promise对象,如果执行异常,会调Promise.reject()方法返回一个promise 对象
async function fn(){
return '111'
}
fn().then(data=>{
console.log(data)//111
})
javascript - 理解 JavaScript 的 async/await_个人文章 - SegmentFault 思否
28.Fetch和Ajax,axios比较优缺点
ajax是一种web数据交互方式
优点:
缺点:
ajax的实现主要四个步骤:
用法:
创建xhr
var xhr=new XMLHTTPRequest()
侦听通信状态改变的事件
xhr.addEventListener("readystatechange",readyStateChangeHandler);
Method 分为 get post put delete等等
Async 异步同步
name和password是用户名和密码
xhr.open(Method,URL,Async,name,password)
发送内容给服务器
xhr.send(内容)
function readyStateChangeHandler(e){
当状态是4时,并且响应头成功200时,
if(xhr.readyState===4 && xhr.status===200){
打印返回的消息
console.log(xhr.response)
}
}
$.ajax({
type: 'POST',
url: url,
data: data,
dataType: dataType,
success: function() {},
error: function() {}
});
axios:基于promise用于浏览器和node.js的http客户端
优点:
用法
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
Fetch基于promise设计的。是原生js,没使用XMLHttpRequest对象,兼容性更好
用法
try {
let response = await fetch(url);
let data = response.json();
console.log(data);
} catch(e) {
console.log("Oops, error", e);
}
29.JS有几种类型的值?能画一下他们的内存图吗?
两种类型的区别是:存储位置不同
引用数据类型在栈中存储了指针、该指针指向堆中该实体的起始地址。
当解析器寻找引用值时、会首先检索其在栈中的地址、取得地址后从堆中获得
30.js事件机制(讲讲事件冒泡和事件捕获以及事件代理,事件委托 ?)
js的事件机制:捕获事件,目标事件,冒泡事件
在事件捕获的概念下发生click事件的顺序应该是
document -> html -> body -> div -> p
js事件机制 - 天之道利而不害 - 博客园
JS中的事件、事件冒泡和事件捕获、事件委托 - 一粒一世界 - 博客园
31.什么是事件代理(事件委托) 有什么好处
就是当事件触发时,把要做的事委托给父元素来处理
优点:减少内存消耗,提高性能;动态绑定事件
缺点:如果把所有事件都用事件代理,可能会出现事件误判
场景1:当多个li标签需要添加点击事件时,当界面上点击li时,会打印它们各自li标签显示的内容。利用事件冒泡的原理,把事件加在父元素(ul)身上!
场景2: 新增元素没有绑定事件的问题(界面上有一个ul里面默认有5个li,并且还有一个按钮,当点击按钮就创建一个新的li,需要不管是默认有的li还是新的li都有点击事件)
//场景1示例
demo
- item1
- item2
- item3
- item4
- item5
代码解析:给5个li标签加了点击事件,当界面上点击li时,会打印它们各自li标签显示的内容。
出现的问题:此时5个li,看起来每个li的点击事件触发时调用的都是同一个函数,即:
function () {
alert(this.innerHTML);
}
解决办法:利用事件冒泡的原理,把事件加在父元素(ul)身上!
var ul = document.getElementsByTagName('ul')[0];
// 只用给ul加点击事件即可
ul.onclick = function (e) { //事件对象在ie8中要通过window.event才能取到,因此e = e || window.event是做兼容处理
e = e || window.event;
// e.target指的是被点击的那个li
alert(e.target.innerHTML);
}
动态绑定事件 因为事件绑定在父级元素 所以新增的元素也能触发同样的事件
32.addEventListener 默认是捕获还是冒泡
默认是冒泡
addEventListener(event,function,useCapture)
document.getElementById("myBtn").addEventListener("click", function(){
document.getElementById("demo").innerHTML = "Hello World";
});
33.原型链
当在一个对象上找一个属性的时候,要先在这个对象本身上找,所有对象上都有个_proto_属性 这个属性值是指向它构造函数的prototype的 如果对象本身没有这个属性 就会去找他的_proto_上找 (也就是构造函数的prototype)如果还没有就去 __proto__的__proto__上找 就这么一直找,找到了则返回,找不到则返回null,这个搜索的过程,就叫做原型链(prototype)
34.原型指针
就是_proto_使指向它构造函数的原型
35.原型和原型链存在的意义是什么?
实例对象可以共享构造函数的原型属性和方法,节省内存,构造函数原型上的属性和方法越多,节省的内存就越大。
作用:构造函数原型上的东西可以让实例们共享,从而节省了空间
36.谈谈JS的运行机制
37.事件循环
主线程从“任务队列”中读取事件,这个过程是不断循环的 ,所以整个的这种运行机制就是事件循环
38.宏任务和微任务 (事件循环机制)
39.setTimeout(fn, 0)
多久才执行,Event Loop
setTimeout 按照顺序放到队列里面,然后等待函数调用栈清空之后才开始执行,这些操作进入队列的顺序,则由设定的延迟时间来决定
说一下eventloop
滑动验证页面
40.浏览器事件流向
事件流 :从页面中接受事件的顺序。
事件流分为两种: 事件捕获,事件冒泡
EventTarget.addEventListener() 方法是将指定的监听器注册到事件目标上,当该对象触发指定的事件时,执行指定的回调函数。
事件目标是一个文档上的元素 Element
, Document
和 Window
或者任何其他支持事件的对象
41.线程和进程是什么?举例说明
进程:拥有资源和独立运行的最小单位(cpu分配资源的最小单位)
线程:建立在进程基础上的一次程序运行单位,一个进程中可以有多个线程(cpu最小的调度单位)
栗子:比如进程=火车,线程就是车厢
一个进程内有多个线程,执行过程是多条线程共同完成的,线程是进程的部分。
一个火车可以有多个车厢
每个进程都有独立的代码和数据空间,程序之间切换会产生较大的开销;线程可以看作轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小。
【多列火车比多个车厢更耗资源】
【一辆火车上的乘客很难换到另外一辆火车,比如站点换乘,但是同一辆火车上乘客很容易从A车厢换到B车厢】
同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
【一辆火车上不同车厢的人可以共用各节车厢的洗手间,但是不是火车上的乘客无法使用别的火车上的洗手间】
为什么js是单线程
JS主要实现浏览器与用户的交互,以及操作DOM。如果JS被设计为多线程,如果一个线程要修改一个DOM元素,另一个线程要删除这个DOM元素,这时浏览器就不知道该怎么办,为了避免复杂的情况产生,所以JS是单线程的。
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
42.javascript的同源策略;跨域? (高频)
同源策略:协议,域名,端口相同
域名:localhost、www.baidu.com
协议:http https ftp
端口:一个网站对应着一个端口, http协议默认端口:80;https协议默认端口是8083
跨域:协议,域名,端口有一样不同都会造成跨域
同源策略带来的麻烦:ajax在不同域名下的请求无法实现,如果说想要请求其他来源的js文件,或者json数据,那么可以通过jsonp来解决
跨域解决方式一
通过CORS解决(最常用的方案,安全,可靠)
跨域资源共享CORS(Cross-Origin Resource Sharing):属于跨源AJAX请求的根本解决方法
Access-Control-Allow-Origin//允许跨域的域名
Access-Control-Allow-Headers//允许的header类型
Access-Control-Allow-Methods//跨域允许的请求方式
xhrFields.withCredentials = true // 前端设置是否带cookie
//java中的 hander() 设置,“*”号表示允许任何域向我们的服务端提交请求:
header("Access-Control-Allow-Origin: *")
3.通过jsonp跨域:服务器与客户端跨源通信的常用方法
// Vue
this.$http.jsonp('http://www.domain2.com:8080/login',{
params:{},
jsonp:'handleCallback'
}).then((res) =>{
console.log(res)
})
3.nginx代理跨域
通过nginx配置一个代理服务器将客户机请求转发给内部网络上的目标服务器;并将服务器上返回的结果返回给客户端。
4.nodejs中间件代理跨域
5.webpack (在vue.config.js文件中)中 配置webpack-dev-server
devServer: {
proxy: {
'/api': {
target: "http://39.98.123.211",
changeOrigin: true, //是否跨域
},
},
},
43.Math的方法
Math.abs()
: 取绝对值; Math.floor()
: 向下取整 Math.ceil()
: 向上取整 console.log(Math.ceil(.95)); // 1Math.max()
: 取最大值 // console.log(Math.max(1,3,2) // 3Math.min()
: 取一组数的最小值 // console.log(Math.min(1,3,2) // 1Math.random()
取随机数,取值范围[0,1) Math.round()
: 四舍五入取整 Math.round(Math.random()*(n-m)+m)
Math.pow()
: 取幂次方 Math.sqrt()
: 开平方;44.如何获取js中三个数的最大值和最小值
var arr = [22,13,6,55,30];
console.log(Math.max(...arr)); // 55
45.看代码给答案。
var a = new Object(); // 引用数据类型
a.value = 1;
b = a; //b.value=1
b.value = 2;//b.value=2;a.value=2,因为a和b指向同一块引用类型的值
alert(a.value); // 2
var a = 1;//基本数据类型
b = a;
b = 2;
console.log(a) //1
46.es5类:用的构造函数;es6类:class类(高频)
es5:
es6:function 类名称(){...} // 与方法不同的是:类名称首字母要大写
// es5 定义类的方法 向类中添加属性
function Person(name){
this.name = name; // 在类内部定义一个属性
this.say = function(){ alter(this.name)}
}
var p1 = new Person('zh')
p1.say()
alert(typeof (p1));//object
alert(typeof (Person('zhh')));//Undefined 函数没有返回值则为Undefined
alert(typeof(Person));//function
// es6 通过class关键字 定义类
class Point{
constructor(x,y){
this.x = x;
this.y = y;
}
toString(){
return '(' + this.x + ',' + this.y + ')'
}
}
1.通过class关键字,可以定于类
2.上面代码定义了一个“类”,里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5的构造函数Point,对应ES6的Point类的构造方法
3、定义“类”的方法时,前面不需要加上function关键字,直接把函数定义放进去就可以了。方法之间不需要逗号分隔,加了会报错。
4.构造函数的prototype属性,在ES6的“类”上面继续存在。类的所有方法都定义在类的prototype属性上面。
class Point {
constructor(){
// ...
}
toString(){
// ...
}
toValue(){
// ...
}
}
// 等同于
Point.prototype = {
toString(){},
toValue(){}
};
5、Object.assign方法可以很方便地一次向类添加多个方法
class Point {
constructor(){
// ...
}
}
Object.assign(Point.prototype, {
toString(){},
toValue(){}
});
JS CLass类 - 天天web学习 - 博客园
47.js的三大特性:继承,封装,多态
1.封装:把公共的属性和功能封装成一个公共的方法,然后通过一个外部可以调用的特定接口进行调用。
好处:
function Person(name , age , sex){
this.name = name ; //共有变量
var age = age ; //私有变量
var sex = sex ; //私有变量
this.show = function (){
console.log(age+"===="+sex);
}
}
var person = new Person('Sunshine',18,'女');
console.log(person.age); // undefined
console.log(person.name); // Sunshine
console.log(person.show()); // 18====女
请看代码后的注释,this指向的都是共有的属性和方法,而直接通过var声明的则属于私有变量(即外部不可访问变量),然后通过一个共有的show方法将私有的age和sex输出。当然show方法也要通过this声明才可以哟,否则的话show方法也是不可访问的。
2.继承:
当多个方法存在相同的属性和方法时,把这些相同的属性和方法提取到一个公共的方法中,通过原型prototype继承该方法。你也可以通过call或apply来继承该方法中的属性和方法。
function Person(name , age , sex){
this.name = name ;
this.age = age ;
this.sex = sex ;
this.show = function (){
console.log( this.age + "========"+ this.sex );
}
}
function Student(name , age , sex , score){
this.score = score ;
Person.apply(this,[name , age , sex]);
}
function Worker(name , age , sex , job){
this.job = job ;
Person.call(this,name , age , sex);
}
Dancer.prototype = new Person('Sunshine',18,'女');
function Dancer(salary ){
this.salary = salary ;
}
var student = new Student('Sunshine',18,'女',100);
var worker = new Worker('Sunshine',18,'女','IT');
var dancer = new Dancer(20000);
console.log(student);
console.log(worker);
console.log(dancer);
当然,个人感觉那个prototype没有说的很好,如果看到这篇博客的你有更好的建议或意见的话,欢迎给我留言。还有call和apply,其实它们的作用是一样的,都是改变this指向,然后它们的区别也可以从代码中看出,传参方式不同。
3.多态 :
在执行同一操作且作用于不同对象时,返回不同的结果 。其实就是把做什么和由谁去做分开,这样使得代码更容易维护,且条例清晰。
function dwn(s){
document.write(s+"
");
}
function Animal(){
this.bite=function(){
dwn("animal bite!");
}
}
function Cat(){
this.bite=function(){
dwn("Cat bite!");
}
}
Cat.prototype=new Animal(); ///inherit Animal
function Dog(){
this.bite=function(){
dwn("Dog bite");
}
}
Dog.prototype=new Animal(); ///inherit Animal
function AnimalBite(animal){
if(animal instanceof Animal) //这里是判断animal的原型是否指向Animal
animal.bite();
}
var cat = new Cat();
var dog = new Dog();
AnimalBite(cat);
AnimalBite(dog);
//最终输出的结果如下:
/*
Cat bite!
Dog bite
*/
Js三大特性--封装、继承以及多态_sunshine的博客-CSDN博客_js 多态
48.原生JS中几种继承方式
原生JS中有四种继承方式,分别为:原型继承,冒充继承,组合继承,寄生组合继承
JS实现继承的几种方法总结_吴迪98的博客-CSDN博客_js继承的几种方法
49.什么是web worker,为什么我们需要他们web worker?
web worker是一种开启线程方式;
原因
50.垃圾回收机制
js觉得这块东西或者变量不需要了 就会发现,然后清除(js自动执行)
怎么解决? 1.标记清除法;2.引用计数:使用完直接赋值为空
内存管理 - JavaScript | MDN
掘金掘金
51.SEO是什么?Search Engine Optimization--搜索引擎优化
根据你输入的内容,浏览器检索出关键字
52.对象的深拷贝?ES6、ES5(高频)
// 浅拷贝
var a = { count: 1, deep: { count: 2 } }
var b = Object.assign({}, a)
// 或者
var b = {...a}
// 浅拷贝的第二种方法
ES6中的Object.assign方法,Object.assign是ES6的新函数。可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
Object.assign(target, ...sources)
参数:
var obj1 = {
a: "hello",
b: {
a: "hello",
b: 21}
};
var cloneObj1= Object.assign({}, obj1);
cloneObj1.a = "changed";
cloneObj1.b.a = "changed";
console.log(obj1.a); //hello
console.log(obj.b.a); // "changed"
// 深拷贝:一般需要借助递归实现,如果对象的值还是个对象,要进一步的深入拷贝,完全替换掉每一个复杂类型的引用。
对象中可能又存在对象,所以需要深拷贝。首先需要知道这是一个递归调用,然后要判断一些特殊类型(数组,正则对象,函数)进行具体的操作,可以通过Object.prototype.toString.call(obj)进行判断。
// 手动复制--一个一个赋值
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }
var deepCopy = (obj) => {
var ret = {}
for (var key in obj) {
var value = obj[key]
ret[key] = typeof value === ‘object‘ ? deepCopy(value) : value
}
return ret
}
53.讲讲Map和Set?(介绍下 Set、Map、WeakSet 和 WeakMap 的区别?)
Set:成员唯一、无序且不重复
[value, value],键值与键名是一致的(或者说只有键值,没有键名)
可以遍历,方法有:add、delete、has;属性size:返回set成员总数
Map
map和set有很大程度是相似的,但是map是以键值对的形式存储数据的。
属性size:返回map结构成员总数
方法:delete(key):删除某个key,返回true;删除失败,返回false;
//Set
数组去重
//Map
map中的size属性
ES6学习总结之Set和Map的使用 - 二郎神杨戬 - 博客园
54.防抖节流
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。(多次触发 只执行最后一次)
应用场景:
实例代码
// 防抖函数
function debounce(fn, wait) {
let timer;
return function() {
let _this = this;
let args = arguments;
if(timer) { clearTimeout(timer) }
timer = setTimeout(function(){
fn.apply(_this, args)
}, wait);
}
}
// 使用
window.onresize = debounce(function() {console.log('resize')}, 500)
应用示例:
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。(规定时间内 只触发一次)
应用场景:
实现思路:
实现代码:
// 方式1: 使用时间戳
function throttle1(fn, wait) {
let time = 0;
return function() {
let _this = this;
let args = arguments;
let now = Date.now()
if(now - time > wait) {
fn.apply(_this, args);
time = now;
}
}
}
// 方式2: 使用定时器
function thorttle2(fn, wait) {
let timer;
return function () {
let _this = this;
let args = arguments;
if(!timer) {
timer = setTimeout(function(){
timer = null;
fn.apply(_this, args)
}, wait)
}
}
}
应用示例:
/* 节流 */
function throttle(func, wait) {
var previous = 0;
return function () {
var now = +new Date();
if (now - previous > wait) {
func.apply(this, arguments);
previous = now;
}
}
}
function getUserAction() {
console.log(`每秒1秒内打印一次`)
}
document.querySelector('div').addEventListener('click', throttle(getUserAction, 1000))
解决方法:加定时器,加loading
55.暂时性死区
let 和 const 声明的变量不存在变量提升,其作用域都是块级作用域,凡是在声明变量之前使用变量就会报错,所以,在代码块内,使用let
命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
if (true) {
// 死区开始
lzp = 'lut'; // ReferenceError
console.log(lzp); // ReferenceError
// 开始声明变量,死区结束
let lzp;
console.log(lzp); // undefined
lzp = 520;
console.log(lzp); // 520
}
56.什么是懒加载和预加载?
懒加载:延迟加载,延迟加载网络资源或符合某些条件时才加载资源。常见的就是图片延时加载。
目的:作为服务器前端的优化,减少请求数或延迟请求数
实现方式:
用setTimeOut或setInterval进行加载延迟.
条件加载,符合某些条件,或触发了某些事件才开始异步下载
可视区加载,即仅加载用户可以看到的区域,主要由监控滚动条来实现,一般会在距用户看到某图片前一定距离遍开始加载,这样能保证用户拉下时正好能看到图片
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。预加载应用如广告弹窗等。
57.src和href
src和href都是用在外部资源的引入上,比如图像,CSS文件,HTML文件,以及其他的web页面等等
区别:
1、请求资源类型不同
理解src:src是source的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置;在请求src资源时会将其指向的资源下载并应用到文档内,例如js脚本,img图片和frame等元素。
2、 浏览器解析方式不同
58.请说出三种减低页面加载时间的方法
59.关于执行环境(上下文)、全局变量对象VO、活动对象AO、全局对象GO
JS引擎在执行代码的过程中需要先解析再执行。
1.初始化全局对象
首先,JS引擎会在执行代码之前,也就是解析代码时,会在我们的堆内存创建一个全局对象:Global Object(简称GO),观察以下代码,在全局中定义了几个变量:
var name = 'curry'
var message = 'I am a coder'
var num = 30
所有的**作用域(scope)**都可以访问该全局对象;
对象里面会包含一些全局的方法和类,像Math、Date、String、Array、setTimeout等等;
其中有一个window属性是指向该全局对象自身的;
该对象中会收集我们上面全局定义的变量,并设置成undefined;
全局对象是非常重要的,我们平时之所以能够使用这些全局方法和类,都是在这个全局对象中获取的;
var GlobalObject = {
Math: '类',
Date: '类',
String: '类',
setTimeout: '函数',
setInterval: '函数',
window: GlobalObject,
...
name: undefined,
message: undefined,
num: undefined
}
2.执行上下文栈(调用栈)
了解了什么是全局对象后,下面就来聊聊代码具体执行的地方。JS引擎为了执行代码,引擎内部会有一个执行上下文栈(Execution Context Stack,简称ECS),它是用来执行代码的调用栈。
(1)ECS如何执行?先执行谁呢?
无疑是先执行我们的全局代码块;
在执行前全局代码会构建一个全局执行上下文(Global Execution Context,简称GEC);
一开始GEC就会被放入到ECS中执行;
4.函数执行上下文
在执行全局代码遇到函数如何执行呢?
在执行的过程中遇到函数,就会根据函数体创建一个函数执行上下文(Functional Execution Context,简称FEC),并且加入到执行上下文栈(ECS)中。
函数执行上下文(FEC)包含三部分内容:
AO:在解析函数时,会创建一个Activation Objec(AO);
作用域链:由函数VO和父级VO组成,查找是一层层往外层查找;
this指向:this绑定的值,在函数执行时确定;
其实全局执行上下文(GEC)也有自己的作用域链和this指向,只是它对应的作用域链就是自己本身,而this指向为window。
5.变量环境和记录
在早期ECMA的版本规范中:每一个执行上下文会被关联到一个变量环境(Variable Object,简称VO),在源代码中的变量和函数声明会被作为属性添加到VO中。对应函数来说,参数也会被添加到VO中。
也就是上面所创建的GO或者AO都会被关联到变量环境(VO)上,可以通过VO查找到需要的属性;
规定了VO为Object类型,上文所提到的GO和AO都是Object类型;
JavaScript的执行过程(深入执行上下文、GO、AO、VO和VE等概念)_MomentYY的博客-CSDN博客_js的go,ao,vo的创建时间
60.defer 和 async区别
单线程是指 JS在执行的时候,有且只有一个主线程来处理所有的任务。目的是为了实现与浏览器交互。
Js中的defer属性和async属性_mengduxiu的博客-CSDN博客_defer js
61.鉴权有了解吗?jwt如果实现踢人,session和jwt鉴权的区别
62.深拷贝的手动实现?
Js基础知识-手动实现深拷贝 - 简书
let obj1={
name:'王',
age:23,
address:{
city:'河南省郑州市'
},
hobby:['play','eat']
}
let obj2=obj1
console.log(obj1.name) // 王
obj2.name='李'
console.log(obj1.name) // 李
// 由于直接将obj1直接赋值给了obj2,此时他们的指针地址是相同的,所以当obj2改变时,obj1的值也会改变。
// 如何将obj1赋值给obj2,修改obj2的值obj1的值不会改变?
1.创建方法deepClone,将返回一个值作为方法的返回值
function deepClone(obj={}){
return obj
}
2.检查传入的参数类型是否为引用类型,不是的话将参数直接返回
function deepClone(obj={}){
if(typeof obj !== 'object' || obj === null){
return obj;
}
}
3.处理参数为引用类型,检测是否为数组,定义新的变量
function deepClone(obj = {}){
if(typeof obj !== 'object' || obj === null){
let result; // 将要返回的变量
if(obj instanceof Array){
result = [] // 如果Obj为数组,则将返回定义为空数组
}else{
result = {} // 如果Obj为对象,则将返回定义为空对象
}
}
}
4.遍历赋值:会用到递归,如果不清楚可以将每一个obj的值进行打印。
function deepClone(obj={}){
if(typeof obj!=='object'||obj==null){
return obj
}
let result // 将要返回的变量
if(obj instanceof Array){
result=[] // 如果Obj为数组,则将返回定义为空数组
}else{
result={} // 如果Obj为对象,则将返回定义为空对象
}
// forin可以用于对象和数组的遍历
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
result[key]=deepClone(obj[key]) // 使用递归
}
}
}
5.再次声明测试
/**
* 深度克隆
* @param {object} obj 需要克隆的对象/数组
*/
function deepClone(obj={}){
console.log(obj)
//obj每次的值
//王 23 {city: "河南省郑州市"} 河南省郑州市 ["play", "eat"] play eat
if(typeof obj!=='object'||obj==null){
return obj
}
let result // 将要返回的变量
if(obj instanceof Array){
result=[] // 如果Obj为数组,则将返回定义为空数组
}else{
result={} // 如果Obj为对象,则将返回定义为空对象
}
// forin可以用于对象和数组的遍历
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
result[key]=deepClone(obj[key]) // 使用递归
}
}
return result
}
let obj2=deepClone(obj1)
obj2.name='李'
console.log(obj1.name,obj2.name) // 王 李
63.手写
Proxy
实现数据劫持
简单来说就是在数据改变时对数据进行监听(处理)
其中Proxy是数据劫持的一种方案
Proxy:
我们可以按照如下方式定义一个 Proxy
const testProxy = new Proxy(target, handler);
target:
要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
handler:
一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 testProxy 的行为
当外界每次对obj进⾏操作时,就会执⾏handler对象的⽅法
handler中常⽤的对象⽅法如下:
get(target, propKey, receiver)
set(target, propKey, value, receiver)
has(target, propKey)
construct(target, args):
apply(target, object, args)
// Proxy是es6新增的,不支持旧版IE
const carInfo = {
brand: '梅赛德斯',
model: 'c500',
price: 5000000,
handlePrice: 0
}
function editPrice(dom){
testProxy.price = dom.value;
}
function handlePrice(dom){
testProxy.handlePrice = dom.value;
}
window.onload = () =>{
document.getElementById('brand').innerHTML = testProxy.brand;
document.getElementById('model').innerHTML = testProxy.model;
document.getElementById('price').innerHTML = testProxy.price;
}
const testProxy = new Proxy(carInfo, {
// 获取对象属性值时触发
// target:原对象,key:对应属性
get: function(target, key) {
console.log(`车辆信息:${key} 被访问`)
return target[key];
},
// 改变/赋值对象属性值时触发
// target:原对象,key:对应属性,newValue:改变的值
set: function(target, key, newValue) {
if (key === "price"){ // 对价格进行拦截校验
if (typeof newValue !== "number") {
alert("价格字段必须为Number类型") // 异常后输入框数值初始化
document.getElementById('price').value = target[key]
return
}else if (newValue < 300000){
alert("价格不能低于300000")
document.getElementById('price').value = target[key]
}else {
target[key] = newValue;
console.log(`车辆信息:${key} 已被更改`)
}
} else if (key === 'handlePrice') {
testProxy.price = target['price'] + parseInt(newValue)
console.log(`价格已被更改 ${testProxy.price}`)
document.getElementById('handlePrice').value = 0;
document.getElementById('price').innerHTML = target['price'];
} else {
target[key] = newValue;
console.log(`车辆信息:${key} 已被更改`)
}
// Proxy仅作为 => 代理 如需要改变原数据,则需要做此赋值操作
// target[key] = newValue
}
})
testProxy.brand = '大众',
testProxy.price = 300001
JavaScript-Proxy & 数据劫持_deft_的博客-CSDN博客_react数据劫持
64.怎么封装请求
1.引入axios
2.创建request.js文件,请求拦截和响应拦截
创建axios实例
import Vue from 'vue'
import axios from 'axios'
import store from '@/store'
const service = axios.create({
timeout: 6000
})
// 请求拦截:token,在请求头中添加key,value
// request interceptor
service.interceptors.request.use(config => {
const token = Vue.ls.get(ACCESS_TOKEN)
if (token) {
// config.headers['Authorization'] = 'bearer ' + token // 让每个请求携带自定义 token 请根据实际情况自行修改
config.headers['Authorization'] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
}
config.headers['account'] = Vue.ls.get(USER_CODE)
config.headers['platform_code'] = '77299c0d776ff54d5a5909784079e6e6'
config.headers['api-version'] = 'v1'
return config
}, err)
// 响应拦截:在响应头加东西,token过期时间,如果token过期直接退出登录
service.interceptors.response.use((response) => {
const str = response.headers['content-disposition']
if (str) {
sessionStorage.setItem('filename', str.split('=')[1])
}
if (response.config.responseType === 'blob') {
return response
}
return response.data
}, err)
3.创建api文件夹-创建对应的js文件,引入request文件,向外暴露请求的方法
// 引入request文件
/**
* 平台管理接口
*/
import { axios } from '@/utils/request'
import { toQueryString } from '@/utils/util'
/**
* 分页查询
* @param parameter
* @returns {*}
*/
export function getUserPage (parameter) {
return axios({
url: process.env.VUE_APP_REQUEST_MANAGE + '/admin/platform/list/page',
method: 'post',
data: parameter
})
}
/**
* 详情
* @param parameter
* @returns {*}
*/
export function byIdDetailRole (parameter) {
return axios({
url: process.env.VUE_APP_REQUEST_MANAGE + '/admin/platform/get',
method: 'get',
params: parameter
})
}
4.在对应页面引入请求方法
65.写代码:求平均数
66.JavaScript不同类型的存储方式有何区别?
普遍认为
67.你说字符串存储在栈内存,那如果字符串很长。超过了栈内存最大容量呢?
所以说我觉得所有数据都存于堆内存,毕竟栈内存容量有限。
68.赋值、深拷贝与浅拷贝有什么不同?
69.class的静态属性,继承?
70.ES5的继承和ES6的继承区别
71.require和import区别
调用时间:
require运行时调用,理论上可以运用代码任何地方,甚至不需要赋值给某个变量之后再使用
import是编译时调用,必须放在文件开头,而且格式也是确定的
规范:
require 是 AMD规范引入方式
import是es6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法
本质
require是赋值过程,其实require 的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量。
import是解构过程。
72.深拷贝几种方法
JSON.stringify;JSON.parse;for...in;递归
73.微前端的几种方案
路由分发:开发成本低,维护成本低,不限技术栈
iframe
单独的时候怎么去访问子框架的地址
web component
74.实现继承的方法有哪些
75.模块化
模块化开发在现代开发中已是必不可少的一部分,它大大提高了项目的可维护、可拓展和可协作性。通常,我们 在浏览器中使用 ES6 的模块化支持,在 Node 中使用 commonjs 的模块化支持。
分类:
76.require 与 import 的区别
77.babel 编译原理
1.babylon 将 ES6/ES7 代码解析成 AST
2.babel-traverse 对 AST 进行遍历转译,得到新的 AST
3.新 AST 通过 babel-generator 转换成 ES5
AST:抽象语法树 (Abstract Syntax Tree),是将代码逐字母解析成 树状对象 的形式。这是语言之间的转换、代码语法检查,代码风格检查,代码格式化,代码高亮,代码错误提示,代码自动补全等等的基础。
78.数组求和最快的方法
eval(arr.join(“+”))
79.JavaScript里面0.1+0.2 === 0.3是false 解决办法
const withinErrorMargin = (left, right) => {
return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}
console.log(withinErrorMargin(0.1 + 0.2, 0.3))
// 第二种方法
console.log(parseFloat((0.1 + 0.2).toFixed(10)) === 0.3)
80.token 的含义
Token 的引入:Token 是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应运而生。
Token 的定义:Token 是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此 Token 返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
使用 Token 的目的:Token 的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
数组拉平的几种方法
如何用 CSS 画一个五角星?
============代码题==========
53.Javascript 如何实现继承?
实例继承:将子构造函数的 prototype 指向父构造函数的一个实例
原型继承:将子构造函数的 prototype 指向父构造函数的 prototype
构造函数绑定:使用 call 或 apply 方法,将父对象的构造函数绑定在子对象上
拷贝继承:如果把父对象的所有属性和方法,拷贝进子对象
ES6 语法 extends:class ColorPoint extends Point {}
11.介绍纯函数
纯函数:一个函数的返回结果只依赖其参数,并且执行过程中没有副作用
30.ajax返回的状态
31.如何实现ajax请求,假如我有多个请求,我需要让这些ajax请求按照某种顺序一次执行,有什么办法呢?如何处理ajax跨域
32.如何实现一个ajax请求?如果我想发出两个有顺序的ajax需要怎么做?
49.Date的实例
36.事件代理和事件委托的区别
https://www.jianshu.com/p/5d7c76eaf214
61.看下列代码,将会输出什么?
考点:1、变量作用域 2、变量声明提升
var foo = 1;
function f(){
console.log(foo); // 它只会找函数里面最近的,代码从上往下加载,所以是undefined
var foo = 2;
console.log(foo); // 2
}
f();
// 输出undefined 和 2。
代码题: 数组去重复,如果考虑复杂类型,考虑引用去重
// 数组去重
// 1. Set
// 2. filter O(n2)
// 3. Map
// 4. js 对象 { }
const arr = [1, 1, 2, 3, '1', x, y, z];
const x = { a: 100 }
const y = { a : 100 }
const z = x
const arr2 = [x, y, z]
function uniq(arr) {
// TODO:
}
如何判断是
new
还是函数调用
function foo() {
// new 调用 or 函数调用
}
new foo();
foo();
// 思路1:new.target
// 思路2:instanceof
// 思路3:constructor
typeof类型判断情况,代码执行情况
let a = 0,b = 0;
function fn(a){
fn = function fn2(b){
alert(++a+b);
}
alert(a++);
}
fn(1); // 1
fn(2); // 5 ??
执行情况
async function async1() {
console.log('async1 start')
await new Promise(resolve => {
console.log('promise1')
})
console.log('async1 success')
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')
// srcipt start
// async1 start
// promise1
// srcipt end
手写数组扁平化函数
解决异步回调的问题
78.Promise中的执行顺序(考察频率:高)
开发者客栈-帮助开发者面试的平台-顽强网络
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
// 解析:上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
111.以下函数调用会发生什么情况
function foo() {
foo();
}
function foo2() {
setTimeout(() => {
foo2();
}, 0);
}
foo(); //会有问题?栈溢出
foo2(); // 会有问题?不会栈溢出
// 什么原因?
下面用的节流:就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
compose???
题目描述:实现一个compose函数
function fn1(x){
retrun x + 1
}
function fn2(x){
retrun x + 2
}
function fn3(X){
return x + 3
}
function fn4(x){
return x + 4
}
const a = compose(fn1,fn2,fn3,fn4)
console.log(a(1)) // 1+4+3+2+1 = 11
实现代码如下:
function compose(...fn) {
if (!fn.length) return (v) => v;
if (fn.length === 1) return fn[0];
return fn.reduce(
(pre, cur) =>
(...args) =>
pre(cur(...args))
);
}
js 事件循环:nodejs 里面的,koa 用的多吗
113.数组扁平化 :将一个多维数组,一层一层的转化为层级较少或者只有一层的数组
题目描述:实现一个方法使用多维数组变成一维数组
最常见的递归版本如下:
function flatter(arr) {
if (!arr.length) return;
return arr.reduce(
(pre, cur) =>
Array.isArray(cur) ? [...pre, ...flatter(cur)] : [...pre, cur],
[]
);
}
// console.log(flatter([1, 2, [1, [2, 3, [4, 5, [6]]]]]));
实现代码如下:
function flatter(arr) {
if (!arr.length) return;
while (arr.some((item) => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
// console.log(flatter([1, 2, [1, [2, 3, [4, 5, [6]]]]]));
114.列表转成树形结构
[
{
id: 1,
text: '节点1',
parentId: 0 //这里用0表示为顶级节点
},
{
id: 2,
text: '节点1_1',
parentId: 1 //通过这个字段来确定子父级
}
...
]
转成
[
{
id: 1,
text: '节点1',
parentId: 0,
children: [
{
id:2,
text: '节点1_1',
parentId:1
}
]
}
]
实现代码如下:
function listToTree(data) {
let temp = {};
let treeData = [];
for (let i = 0; i < data.length; i++) {
temp[data[i].id] = data[i];
}
for (let i in temp) {
if (+temp[i].parentId != 0) {
if (!temp[temp[i].parentId].children) {
temp[temp[i].parentId].children = [];
}
temp[temp[i].parentId].children.push(temp[i]);
} else {
treeData.push(temp[i]);
}
}
return treeData;
}
115.树形结构转成列表
[
{
id: 1,
text: '节点1',
parentId: 0,
children: [
{
id:2,
text: '节点1_1',
parentId:1
}
]
}
]
转成
[
{
id: 1,
text: '节点1',
parentId: 0 //这里用0表示为顶级节点
},
{
id: 2,
text: '节点1_1',
parentId: 1 //通过这个字段来确定子父级
}
...
]
代码实现如下:
function treeToList(data) {
let res = [];
const dfs = (tree) => {
tree.forEach((item) => {
if (item.children) {
dfs(item.children);
delete item.children;
}
res.push(item);
});
};
dfs(data);
return res;
}
116.几种常见的JS递归算法题
递归的概念:就是函数子级调用自己本身,或者在自己函数调用的下级函数中调用自己
递归的步骤:
经典案例1:求和
// 求1-100的和
function sum(n){
if(n==1) retrun 1;
return sum(n-1) + 1; // 为什么把1单拿出来,因为sun(1-1)==>sum(0)等于0,这里求的是1-100的和,一直是0就变成了死循环一直都是1。
}
经典案例2:斐波拉契数列(黄金分割数列,兔子数列)???不懂
// 1,1,2,3,5,8,13,21,34,55,89...求第n项
// 递归方法
function fib(n){
if( n===1 || n ===2 ) return n-1;
return fib(n - 1) + fib(n - 2)
}
// 非递归方法
function fib(n){
let a = 0;
let b = 1;
let c = a + b;
for(let i = 3; i < n;i++){
a = b;
b = c;
c = a + b;
}
return c;
}
console.log(fib(10));
案例3:深拷贝
原理:clone(o) = new Object(),返回一个对象
function clone(o){
var temp = {}
for(var key in o){
if(typeof o[key] === 'object'){
temp[key] = clone[o(key])]
}else{
temp[key] = o[key]
}
}
return temp;
}
下方js执行后的打印值为?
function demo() {
this.length = 10;
var fn = function() {
console.log(this.length); // 输出多少?
}
arr = [fn, 'hello layui'];
fn.length = 100;
arr0;
}
window.demo()
console.log(this.length)------打印结果为2
请说出以下结果输出什么?为什么?
for(var i = 0; i < 5; i++) {
setTimeout(function(){
console.log(i)
}, 0)
}
答案:5个5
解释:异步代码需要等同步代码先执行,所以当异步定时器执行时,
同步的for循环已经循环完毕
请说出以下flag的结果?为什么?
function show(){}
function getName() { return '牛夫人' }
var flag = show() || getName()
答案:flag值为'牛夫人'
解释:1.函数都会有一个默认的返回值undefined
2.逻辑或如果第一个值成立就直接返回第一个值,否则直接返回第二个值
执行下面代码打印什么?为什么?
var a = {};
var b = {key: 'b'};
var c = {key: 'c'};
var d = [3,5,6];
a[b] = 123;
a[c] = 345;
a[d] = 333;
console.log(a[b]);
console.log(a[c]);
console.log(a[d]);
console.log(a[b]); // 打印:345
console.log(a[c]); // 打印:345
console.log(a[d]); // 打印:333
为什么:对象转化字符串会变成一个'[object Object]'
请简述Js Bridge
请说一下SSR的单机QPS
请说一下eggJs的初始化原理
108.函数柯里化:就是把多参数函数转化为单参数函数
// 没有柯里化
function sum(a,b,c){
return a + b +
}
console.log(sum(1,2,3)) // 6
// 转化成柯里化
function sum(a){
return function(b){
return function(c){
return a + b + c
}
}
}
console.log(sum(1)(2)(3)) // 6
// 简化
const sum = a => b => c => a + b + c
console.log(sum(1)(2)(3)) // 6
解决遍历对象时,把原型上的属性遍历出来了咋办?
使用hasOwnProperty
判断
function Person(name) {
this.name = name
}
Person.prototype.age = 23
const person = new Person('Sunshine_lin')
for (const key in person) { console.log(key) } // name age
// 使用 hasOwnProperty
for (const key in person) {
person.hasOwnProperty(key) && console.log(key)
} // name
109.用Set获取两个数组的交集,如何做
js数据结构set 取交集和并集_尤小白的博客-CSDN博客_js set取交集
let a = new Set([1,2,3]);
let b = new Set([4,3,2]);
// 并集
let union = [...new Set([...a,...b])];
console.log(union); // 1,2,3,4
// 交集
let intersect = [...new Set([...a].filter((x) => b.has(x)))];
console.log(intersetct) // 2,3
110.手写实现Promise.all
回答以下代码,alert的值分别是多少?
怎么降维数组 [[1,2],[3,4]] --> [1, 2, 3, 4]
102.数组的冒泡排序
var ary = [12,32,56,67,88,99,101];
// 1. sort
/*ary.sort(function (a,b) {
return a-b;// 从小到大
});*/
// 实现从小到大
// 让相邻两项进行比较,如果前面一项比后面一项大,那么让这两项互换位置;如果前比后面一项小,那么不换位置;每循环一整轮,把数组的最大值放到数组的最后面;有多少项,就执行多少次这样的循环;
for(var j = 0;jary[i+1]){//前面一项比后面大
// 借助第三方变量
//如果能进来,说明数组没有排好顺序;如果进不来,说明数组已经是排好的
var temp = ary[i];
ary[i] = ary[i+1];
ary[i+1] = temp;
flag = false;
}
}
if(flag){
break;
}
}
console.log(ary);
100.数组的快速排序(
快速排序快速排序的原理:
首先取原有数组中的中间项;接下来循环原有的数组每一项,和中间项进行比较,如果比中间项的小的放在左边的数组中,比中间项大的放在右边的数组中;然后再对左边和右边数组进行刚才的操作;最后把所有的小数组和中间项串在一起就是排好的数组)
var ary = [12,8,89,78,76,56,25,35];
function quickSort(ary) {
console.log(ary);
// 当数组的长度小于等于1;直接return当前的数组;
if(ary.length<=1){
return ary;
}
//获取
var middleIndex = Math.floor(ary.length/2);
//删除数组中中间项;并且获取中间项的值
var middleVal = ary.splice(middleIndex,1)[0];
console.log(middleVal);
var left = [];
var right = [];
for(var i=0;i
如何实现数组的复制
// 逐一复制
var arr1=[];
for(var i=0;i
(32条消息) JS高级_前端-公瑾的博客-CSDN博客_js高级是什么
最全的手写JS面试题 - 掘金
80% 应聘者都不及格的 JS 面试题 - 掘金
最全的手写JS面试题 - 掘金
104.JSONP的优缺点
jsonp是json的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
优点:
缺点:
82.(微医)Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?
promise构造函数是同步执行的,then方法是异步执行的
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
// 执行结果是:1243
83. (滴滴、挖财、微医、海康)JS 异步解决方案的发展历程以及优缺点。
1. 回调函数(callback)
setTimeout(() => {
// callback 函数体
}, 1000)
缺点:回调地狱,很难处理错误。不能用 try catch 捕获错误,不能 return
回调地狱的根本问题在于
缺乏顺序性: 回调地狱导致的调试困难,和大脑的思维方式不符
嵌套函数存在耦合性,旦有所改动,就会牵一发而动全身,即(控制反转,嵌套函数过多的多话,很难处理错误
ajax('XXX1', () => {
// callback 函数体
ajax('XXX2', () => {
// callback 函数体
ajax('XXX3', () => {
// callback 函数体
})
})
})
优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。)
2. Promise
Promise就是为了解决callback的问题而产生的。
Promise 实现了链式调用,也就是说每次 then 后返回的都是一个全新 Promise,如果我们在 then 中 return ,return 的结果会被Promise.resolve() 包装
优点:解决了回调地狱的问题
ajax('XXX1')
.then(res => {
// 操作逻辑
return ajax('XXX2')
}).then(res => {
// 操作逻辑
return ajax('XXX3')
}).then(res => {
// 操作逻辑
})
缺点:无法取消 Promise ,错误需要通过回调函数来捕获
3. Generator
特点:可以控制函数的执行,可以配合 co 函数库使用
function* fetch() {
yield ajax('XXX1', () => { })
yield ajax('XXX2', () => { })
yield ajax('XXX3', () => { })
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()
4. Async / await
async、await 是异步的终极解决方案
优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题
缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。85.说几条写JavaScript的基本规范?
13.什么是 “use strict”; ? 使用它的好处和坏处分别是什么?
ECMAScript5中引入的严格模式,通过让JavaScript运行环境对一些开发过程中最常见和不易发现的错误做出和当前不同的处理,来让开发者拥有一个”更好”的JavaScript语言。
好处:
坏处:
"use strict"是ECMAscript5添加的严格运行模式、这种模式使得JavaScript在更严格的条件下运行、使JS编码更加规范化的模式,消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为。
提高编译器效率,增加运行速度;
为未来新版本的Javascript标准化做铺垫。