JavaScript面试题(二)

简介: JavaScript面试题(二)

JavaScript面试题(一) https://developer.aliyun.com/article/1399303


31.★★★ 如何用 setTImeout 来实现 setInterval?

1.不去关心回调函数是否还在运行
在某些情况下,函数可能需要比间隔时间更长的时间去完成执行。比如说是用setInterval每隔5秒对远端服务器进行轮询,网络延迟,服务器无响应以及其他因素将会阻止请求按时按成。结果会导致返回一串无必要的排成队列请求。
2.忽视错误
因为某些原因,setInterval调用的代码中会出现一个错误,但是代码并不会中止执行而是继续执行错误的代码。
3.缺乏灵活性
除了前面提到的缺点之外,我非常希望setInterval方法能有一个表明执行次数的参数而不是无休止的执行下去。
function interval(func, w, t){
    var interv = function(){
        if(typeof t === "undefined" || t-- > 0){
            setTimeout(interv, w);
            try{
                func.call(null);
            }
            catch(e){
                t = 0;
                throw e.toString();
            }
        }
    };
    setTimeout(interv, w);
};


32.★★★ 如何判断 user 对象里有没有 a 这个属性?如果把user对象中所有的属性都输出出来?(var user = {‘a’: ‘19’, ‘b’: ‘18’, ‘c’: ‘16’})

如何判断 user 对象里有没有 a 这个属性?
js对象的Object.hasOwnProperty()方法
返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。
let obj = new Object();
obj.a = "123";
console.log(obj.hasOwnProperty('a'))  // true
console.log(obj.hasOwnProperty('b'))  // false
把user对象中所有的属性都输出出来
for(item for user){
    console.log(item)
}


33.★★ 使用 setTimeout 模拟 setInterval 的功能做一个60秒的倒数计时

function setInter(s,fn){
  let timeOut = (s,fn)=>{
      setTimeout(()=>{
        fn();
        timeOut(s,fn);
      },s)
  }
  timeOut(s,fn);
}
//调用上面的方法
setInter(60000,()=>{console.log("hello world!")})


34.★★★ 实现一个函数 add(),运算结果可以满足如下预期结果

add(1)(2) //3

add(1,2,3)(10) //16

add(1)(2)(3,4)(5) //15

function add () {
  var args = Array.prototype.slice.call(arguments);
  var fn = function () {
    var sub_arg = Array.prototype.slice.call(arguments);
   // 把全部的参数聚集到参数的入口为一个参数: args.concat(sub_arg)
    return add.apply(null, args.concat(sub_arg));
  }
  fn.valueOf = function () {
  return args.reduce(function(a, b) {
      return a + b;
    })
  }
  return fn;
}
add(1,2,3)(10) //16
add(1)(2)(3,4)(5) //15


35.★★★ 如何避免回调地狱?

Promise 对象就是为了解决这个问题而提出的。它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套,改成链式调用。


promise只有两个状态resolve和reject,当它触发任何一个状态后,它会将当前的值缓存起来,并在有回调函数添加进来的时候尝试调用回调函数,如果这个时候还没有触发resolve或者reject,那么回调函数会被缓存,等待调用,如果已经有了状态(resolve或者reject),则立刻调用回调函数。并且所有回调函数在执行后都立即被销毁。


ES6 co/yield方案

yield: Generator 函数是协程在 ES6 的实现,而yield是 Generator关键字, 异步操作需要暂停的地方,都用yield语句注明。

co: co 模块是著名程序员 TJ Holowaychuk 于 2013 年 6 月发布的一个小工具,用于 Generator 函数的自动执行。


ES7 async/await 方案

async/await是es7的新标准,并且在node7.0中已经得到支持。

它就是 Generator 函数的语法糖,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。可以理解官方对co和Generator 封装方案。


36.★★ 写一个 function,清除字符串前后的空格。(兼容所有的浏览器)

function trim(str) {
    if (str && typeof str === "string") {
        return str.replace(/(^\s*)|(\s*)$/g,""); //去除前后空白符
    }
}


37.★★ 使用正则表达式验证邮箱格式。

function fChkMail(emailAddress){ 
    var reg = new RegExp("^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$"); 
    var bChk=reg.test(emailAddress); 
    return bChk; 
}


38.★★★ 简述同步和异步的区别

同步:

同步的思想是:所有的操作都做完,才返回给用户。这样用户在线等待的时间太长,给用户一种卡死了的感觉(就是系统迁移中,点击了迁移,界面就不动了,但是程序还在执行,卡死了的感觉)。这种情况下,用户不能关闭界面,如果关闭了,即迁移程序就中断了。


异步:

将用户请求放入消息队列,并反馈给用户,系统迁移程序已经启动,你可以关闭浏览器了。然后程序再慢慢地去写入数据库去。这就是异步。但是用户没有卡死的感觉,会告诉你,你的请求系统已经响应了。你可以关闭界面了。


同步和异步本身是相对的:

同步就相当于是 当客户端发送请求给服务端,在等待服务端响应的请求时,客户端不做其他的事情。当服务端做完了才返回到客户端。这样的话客户端需要一直等待。用户使用起来会有不友好。


异步就是,当客户端发送给服务端请求时,在等待服务端响应的时候,客户端可以做其他的事情,这样节约了时间,提高了效率。存在就有其道理 异步虽然好 但是有些问题是要用同步用来解决,比如有些东西我们需要的是拿到返回的数据在进行操作的。这些是异步所无法解决的。


39.★★ JavaScript 中 callee 和 caller 的作用

callee

callee是对象的一个属性,该属性是一个指针,指向参数arguments对象的函数

作用:就是用来指向当前对象

返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文.

callee是arguments 的一个属性成员,它表示对函数对象本身的引用,这有利于匿名

函数的递归或者保证函数的封装性


caller

caller是函数对象的一个属性,该属性保存着调用当前函数的函数的引用(指向当前函数的直接父函数)

返回一个对函数的引用,该函数调用了当前函数。

functionName.caller

functionName 对象是所执行函数的名称。


注意:

对于函数来说,caller 属性只有在函数执行时才有定义。 如果函数是由 Javascript 程序的顶层调用的,那么 caller 包含的就是 null 。


40.★★ 统计字符串中字母个数或统计最多的字母数。

function count(str) {
  var obj = {}; // 统计对象
  var i = 0;
  var len = str.length;
  for (; i < len; i++){
    var curChar = str.charAt(i); 
    // 如果结果对象存在该字符的属性,则自增,否则置为1
    if (obj[curChar]) {
      obj[curChar]++;
    } else {
      obj[curChar] = 1;
    }
  }
  // 返回结果
  return obj;
}
var str = "javaScript";
console.log(count(str));


41.★★★ jQuery 的事件委托方法 on,live,delegate之间有区别?

live 把事件委托交给了document(根节点),document 向下去寻找符合条件的元素(),

不用等待document加载结束也可以生效。


delegate可指定事件委托对象,相比于live性能更优,直接锁定指定选择器;


on事件委托对象选填,如果不填,即给对象自身注册事件,填了作用和delegate一致。


42.★★★ 简述下 Promise 对象

Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理更强大。


所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件 (通常是一个异步操作)的结果。

从语法上说,Promise是一个对象,从它可以获取异步操作的消息。


Promise对象有以下2个特点:


1.对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。


2**.一旦状态改变,就不会再变,任何时候都可以得到这个结果**。Promise对象的状态改变,只有两种可能:从Pending变为Resolved;从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象田静回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

有了Promise对象,就可以把异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供了统一的接口,使得控制异步操作更加容易。


43.★★★ 数组扁平化,不用 api

function myFlat(arr){
    let res = [];
    for(let i=0; i<arr.length; i++){   
        if(arr[i] instanceof Array){
            res = res.concat(myFlat(arr[i]));
        }else {
            res.push(arr[i]);
        }
    }
    return res;
}
let arr = [1,[2,3,[4,5]]];
console.log(myFlat(arr))


44.★★★ 用 JavaScript 实现观察者模式

function BusinessOne(name){
    this.name = name;
    //订阅者的集合
    this.subscribers = new Array();
}
//订阅者的发送消息的方法(推模式)
BusinessOne.prototype.delive = function(news){
    var self = this;
    //给每一个订阅者发送消息
    this.subscribers.forEach(
        function(fn){
            //调用接受者处理信息的函数
            fn(news,self);
        }
    )
}
//扩展公共订阅的函数,和取消订阅的函数
Function.prototype.subscribe = function(publisher){
    var that = this;
    //some 访问数组度i型并且以参数的形式传回回调函数中
    //只要至少有一次返回是true那么some就是true
    var alreadyExists = publisher.subscribers.some(
        function(el){
            //处理不能重复订阅的功能
            if(el == that){
                return;
            }
        }
    );
    //没用订阅你就可以订阅
    if(!alreadyExists){
        publisher.subscribers.push(that);
    }
    return this;
}
//取消
Function.prototype.unsubscribe = function(publisher){
    var that = this;
    publisher.subscribers = publisher.subscribers.filter(
        function(el){
            if(el !== that){
                return el;
            }
        }
    );
    return this;
};  


45.★★ 简述一下面象对象的六法则

1. 单一职责原则:一个类只做它该做的事情

2. 开闭原则:软件实体应当对扩展开放,对修改关闭

3. 依赖倒转原则:面向接口编程

4. 接口隔离原则:接口要小而专,绝不能大而全

5. 合成聚合复用原则:优先使用聚合或合成关系复用代码

6. 迪米特法则:迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解(低耦合)


46.★★★ 谈谈垃圾回收机制方法以及内存管理

垃圾回收方式

① 标记清除

工作原理:是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。


② 引用计数

工作原理:跟踪记录每个值被引用的次数。一旦没有引用,内存就直接释放了。


什么时候触发垃圾回收?

垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。

1、合理的GC方案:(1)、遍历所有可访问的对象; (2)、回收已不可访问的对象。

2、GC缺陷: (1)、停止响应其他操作;

3、GC优化策略: (1)、分代回收(Generation GC);(2)、增量GC


47.★★★ 开发过程中遇到内存泄漏的问题都有哪些?

1、当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露。

2、由于是函数内定义函数,并且内部函数–事件回调的引用外暴了,形成了闭包。闭包可以维持函数内局部变量,使其得不到释放。


48.★★★ 请编写获取当前窗口地址中查询参数name的值,当前窗口地址为:https://foo.com/?id=1&name=tom

function GetQueryString(name){
    var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
    var r = window.location.search.substr(1).match(reg);
    if(r!=null)
        return unescape(r[2]); 
    return null;
}


49.★★★ 已知a,b两个构造函数,现在 let c = new a(),如何在c的存储地址不变的情况下,改变c的继承(c->a 转为 c->b)


1. 改变原型链:通过改变C的prototype为b,实现内存地址不动,改变继承


50.★★★ 浏览器有哪些兼容问题,你封装过什么插件

//1.滚动条到顶端的距离(滚动高度)
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
//2.滚动条到左端的距离
var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
//3. IE9以下byClassName
function byClassName(obj,className){
    //判断是否支持byClassName
    if(obj.getElementsByClassName){
        //支持
        return obj.getElementsByClassName(className);
    }else{
        //不支持
        var eles = obj.getElementsByTagName('*'); //获取所有的标签
        var arr = []; //空数组,准备放置找到的对象
        //遍历所有的标签
        for(var i = 0,len = eles.length;i < len;i ++){
            //找出与我指定class名相同的对象
            if(eles[i].className === className){
                arr.push(eles[i]); //存入数组
            }
        }
        return arr; //返回
    }
}
//4. 获取非行内样式兼容    IE:currentStyle  标准:getComputedStyle
function getStyle(obj,attr){
    return window.getComputedStyle ? getComputedStyle(obj,true)[attr] : obj.currentStyle[attr];
}
//div.style.width =  '';设置样式
//obj['属性']: 对象是变量时,必须用对象['属性']获取。
//5. 获取事件对象的兼容
evt = evt || window.event
//6. 获取鼠标编码值的兼容
function getButton(evt){
    var e = evt || window.event;
    if(evt){
        return e.button;
    }else if(window.event){
        switch(e.button){
            case 1 : return 0;
            case 4 : return 1;
            case 2 : return 2;
        }
    }
}
//7. 获取键盘按键编码值的兼容
var key = evt.keyCode || evt.charCode || evt.which;
//8. 阻止事件冒泡的兼容
e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
//9. 阻止超链接的默认行为的兼容
evt.preventDefault ? evt.preventDefault() : evt.returnValue = false;
//10. 添加事件监听器的兼容
function addEventListener(obj,event,fn,boo){
    if(obj.addEventListener){
        obj.addEventListener(event,fn,boo);
    }else if(obj.attachEvent){
        obj.attachEvent('on' + event,fn);
    }
}
//11. 移除事件监听器的兼容
function removeEventListener(obj,event,fn,boo){
    if(obj.removeEventListener){
        obj.removeEventListener(event,fn,boo);
    }else if(obj.detachEvent){
        obj.detachEvent('on' + event,fn);
    }
}
//12. 获取事件源的兼容
var target = event.target || event.srcElement;


51.★★★ 如何判断一个对象是否为数组,函数

方法一: instanceof:
var arr=[];
console.log(arr instanceof Array) //返回true
方法二: constructor:
console.log(arr.constructor == Array); //返回true
方法三: Array.isArray()
console.log(Array.isArray(arr)); //返回true


52.★★★ 写一个函数,接受可变个数参数,且每个参数均为数字,返回参数的最大值。

function myMax(){
    return Math.max(arguments)
}


53.★★★ 请写出 ES6 Array.isArray()

if (!Array.isArray){
  Array.isArray = function(arg){
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}


54.★★★ 实现一个函数 clone,可以对 JavaScript 中的5种主要数据类型进行值复制。

// 方法一:
Object.prototype.clone = function() {
    var o = this.constructor === Array ? [] : {};
    for (var e in this) {
        o[e] = typeof this[e] === "object" ? this[e].clone() : this[e];
    }
    return o;
};
//方法二:
/**
     * 克隆一个对象
     * @param Obj
     * @returns
     */
function clone(Obj) {
    var buf;
    if (Obj instanceof Array) {
        buf = []; //创建一个空的数组
        var i = Obj.length;
        while (i--) {
            buf[i] = clone(Obj[i]);
        }
        return buf;
    } else if (Obj instanceof Object) {
        buf = {}; //创建一个空对象
        for (var k in Obj) {
            //为这个对象添加新的属性
            buf[k] = clone(Obj[k]);
        }
        return buf;
    } else {
        //普通变量直接赋值
        return Obj;
    }
}


55.★★★ 假如A页面我定义了一个定时器,然后跳到B页面如果让A页面的定时器暂停

方法1:在beforeDestroy()等生命周期结束阶段内清除定时器:
beforeDestroy() {
    clearInterval(this.timer);
    this.timer = null;
}
方法2:通过$once这个事件侦听器器在定义完定时器之后的位置来清除定时器。
const timer = setInterval(() =>{
    // 某些定时器操作
}, 500);
// 通过$once来监听定时器,在beforeDestroy钩子可以被清除。
this.$once('hook:beforeDestroy', () => {
    clearInterval(timer);
})


56.★★★ promise的实现原理,如果我现在向服务器发送一个请求,但是我后悔了,不想让服务器返回数据,去实现一个delay

取消结束Promise的方法?
1. 返回一个pending状态的Promise,原Promise链会终止
Promise.resolve().then(() => {
    console.log('ok1')
    return new Promise(()=>{})  // 返回“pending”状态的Promise对象
}).then(() => {
    // 后续的函数不会被调用
    console.log('ok2')
}).catch(err => {
    console.log('err->', err)
})
2. Promise.race竞速方法
let p1 = new Promise((resolve, reject) => {
    resolve('ok1')
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {resolve('ok2')}, 10)
})
Promise.race([p2, p1]).then((result) => {
    console.log(result) //ok1
}).catch((error) => {
    console.log(error)
})
3. 当Promise链中抛出错误时,错误信息沿着链路向后传递,直至捕获
Promise.resolve().then(() => {
    console.log('ok1')
    throw 'throw error1'
}).then(() => {
    console.log('ok2')
}, err => {     
    // 捕获错误
    console.log('err->', err)
}).then(() => {   
    // 该函数将被调用
    console.log('ok3')
    throw 'throw error3'
}).then(() => {
    // 错误捕获前的函数不会被调用
    console.log('ok4')
}).catch(err => {
    console.log('err->', err)
Axios如何取消请求?
第一种通过CancelToken.source工厂方法创建cancel token
var CancelToken = axios.CancelToken;
var source = CancelToken.source();
axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // 处理错误
  }
});
// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
第二种通过传递executor函数到CancelToken的构造函数来创建cancel token
var CancelToken = axios.CancelToken;
var cancel;
axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
});
// 取消请求
cancel();


57.★★★ CommonJS 和 RequireJS 的实现原理

commonjs是通过module.exports导出模块,用require引入一个模块,原理:闭包
requirejs是通过define定义导出模块,用require引入模块。
define('name',[],function(){
    return 'requirejs'
})
define('say',['name'].function(name){
    return "my name is" + name
})
require(['say'],function(text){
    console.log(text)
})


58.★★★ 面向对象编程与面向过程编程的区别?

面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。

为了简化程序设计,面向过程把函数继续切分为子函数,

即把大块函数通过切割成小块函数来降低系统的复杂度。


面向对象的程序设计把计算机程序视为一组对象的集合,

而每个对象都可以接收其他对象发过来的消息,并处理这些消息,

计算机程序的执行就是一系列消息在各个对象之间传递。


59.★★★ eval 是做什么的?性能怎么样?安全如何?

它的功能是把对应的字符串解析成js代码并运行,


应该避免使用eval,因为不安全,非常耗性能(2次,一次解析成js语句,一次执行)


注意:在项目里写js代码的时候,禁止使用的,因为有安全因素。


60.★★★★★ 函数节流、防抖。scroll resize 使用函数节流实现不要频繁触发事件的需求。

防抖:
//scroll方法中的do somthing至少间隔500毫秒执行一次
    window.addEventListener('scroll',function(){
        var timer;//使用闭包,缓存变量
        return function(){
            if(timer) clearTimeout(timer);
            timer = setTimeout(function(){
                console.log('do somthing')
            },500)
        }
    }());//此处()作用 - 立即调用return后面函数,形成闭包
节流:
//scroll方法中当间隔时间大于2s,do somthing执行一次
    window.addEventListener('scroll',function(){
        var timer ;//使用闭包,缓存变量
        var startTime = new Date();
        return function(){
            var curTime = new Date();
            if(curTime - startTime >= 2000){
                timer = setTimeout(function(){
                    console.log('do somthing')
                },500);
                startTime = curTime;
            }
        }
    }());//此处()作用 - 立即调用return后面函数,形成闭包


JavaScript面试题(三)  https://developer.aliyun.com/article/1399381

相关文章
|
2月前
|
JSON JavaScript 前端开发
Javascript基础 86个面试题汇总 (附答案)
该文章汇总了JavaScript的基础面试题及其答案,涵盖了JavaScript的核心概念、特性以及常见的面试问题。
50 3
|
2月前
|
前端开发 JavaScript
JavaScript 面试系列:如何理解 ES6 中 Generator ?常用使用场景有哪些?
JavaScript 面试系列:如何理解 ES6 中 Generator ?常用使用场景有哪些?
|
3月前
|
JavaScript 前端开发
常见的JS面试题
【8月更文挑战第5天】 常见的JS面试题
61 3
|
12天前
|
JSON JavaScript 前端开发
[JS]面试官:你的简历上写着熟悉jsonp,那你说说它的底层逻辑是怎样的?
本文介绍了JSONP的工作原理及其在解决跨域请求中的应用。首先解释了同源策略的概念,然后通过多个示例详细阐述了JSONP如何通过动态解释服务端返回的JavaScript脚本来实现跨域数据交互。文章还探讨了使用jQuery的`$.ajax`方法封装JSONP请求的方式,并提供了具体的代码示例。最后,通过一个更复杂的示例展示了如何处理JSON格式的响应数据。
25 2
[JS]面试官:你的简历上写着熟悉jsonp,那你说说它的底层逻辑是怎样的?
|
30天前
|
Web App开发 JavaScript 前端开发
前端Node.js面试题
前端Node.js面试题
|
3月前
|
存储 JavaScript 前端开发
2022年前端js面试题
2022年前端js面试题
38 0
|
3月前
|
JavaScript 前端开发 程序员
JS小白请看!一招让你的面试成功率大大提高——规范代码
JS小白请看!一招让你的面试成功率大大提高——规范代码
|
3月前
|
存储 JavaScript 前端开发
JS浅拷贝及面试时手写源码
JS浅拷贝及面试时手写源码
|
3月前
|
JavaScript 前端开发
JS:类型转换(四)从底层逻辑让你搞懂经典面试问题 [ ] == ![ ] ?
JS:类型转换(四)从底层逻辑让你搞懂经典面试问题 [ ] == ![ ] ?
|
4月前
|
缓存 JavaScript 前端开发
js高频面试题,整理好咯
中级前端面试题,不低于12k,整理的是js较高频知识点,可能不够完善,大家有兴趣可以留言补充,我会逐步完善,若发现哪里有错,还请多多斧正。