IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> JavaScript面试真题 -> 正文阅读

[JavaScript知识库]JavaScript面试真题

1. ★★ 介绍一下JS的内置类型有哪些?

1. 空类型:null
2. 未定义:undefined
3. 布尔:boolean
4. 数字:number
5. 字符串:string
6. 符号:symbol(ES6新增)
7. 对象:object
除了对象之外,其他为基本类型.

2. ★★★★ 介绍一下 typeof 区分类型的原理

typeof原理: 不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位存储其类型信息。

000: 对象
010: 浮点数
100:字符串
110: 布尔
1: 整数
/*----------------------------------------------*/
typeof null 为"object", 原因是因为 不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位都为0的话会被判断为Object类型,null的二进制表示全为0,自然前三位也是0,所以执行typeof时会返回"object"

3. ★★★ 介绍一下类型转换

/*-------------------显式转换---------------------*/
1. toString()      // 转化为字符串,不可以转null和underfined
2. Number()     // 转换为数字,字符串中有一个不是数值的字符,返回NaN
3. parseInt()    // 转换为数字,第一个字符不是数字或者符号就返回NaN
4. String()     // 转换为字符串, 
5. Boolean()     // 转换为布尔值
/*-------------------隐式转换(+-)---------------------*/
当 JavaScript 尝试操作一个 "错误" 的数据类型时,会自动转换为 "正确" 的数据类型
1. num  +  ""  -> String
2. num + bool -> num
// 当加号运算符时,String和其他类型时,其他类型都会转为 String;其他情况,都转化为Number类型

3. string - num -> num
// 其他运算符时, 基本类型都转换为 Number,String类型的带有字符的比如: 
4. 'a1' - num -> NaN
// 与undefined 一样。

/*-------------------隐式转换(逻辑表达式)---------------------*/

1. 对象和布尔值比较
对象和布尔值进行比较时,对象先转换为字符串,然后再转换为数字,布尔值直接转换为数字
[] == true;  //false  []转换为字符串'',然后转换为数字0,true转换为数字1,所以为false
2. 对象和字符串比较
对象和字符串进行比较时,对象转换为字符串,然后两者进行比较。
[1,2,3] == '1,2,3' // true  [1,2,3]转化为'1,2,3',然后和'1,2,3', so结果为true;
3. 对象和数字比较
对象和数字进行比较时,对象先转换为字符串,然后转换为数字,再和数字进行比较。
[1] == 1;  // true  `对象先转换为字符串再转换为数字,二者再比较 [1] => '1' => 1 所以结果为true
4. 字符串和数字比较
字符串和数字进行比较时,字符串转换成数字,二者再比较。
'1' == 1 // true
5. 字符串和布尔值比较
字符串和布尔值进行比较时,二者全部转换成数值再比较。
'1' == true; // true 
6. 布尔值和数字比较
布尔值和数字进行比较时,布尔转换为数字,二者比较。
true == 1 // true

4. ★★★★ 说说你对 JavaScript 的作用域的理解。什么是作用域链?

在 JavaScript 中有两种作用域类型:

1. 局部作用域:只能在函数内部访问它们
2. 全局作用域:网页的所有脚本和函数都能够访问它
JavaScript 拥有函数作用域:每个函数创建一个新的作用域。

作用域决定了这些变量的可访问性(可见性)。

函数内部定义的变量从函数外部是不可访问的(不可见的)。

作用域链:
当查找变量的时候,会先从当前上下文的变量对象中查找,
如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。
这样由多个执行上下文的变量对象构成的链表就叫做作用域链

5. ★★ 解释下 let 和 const 的块级作用域

/*------------let-----------*/
1. let声明的仅在块级作用域内有效,
2. let不会发生变量提升的现象,所以一定要在定义后使用,否则报错。
3. 暂时性死区:只要块级作用域内存在let命令,它所声明的变量就绑定这个区域,不再受外部影响。
4. 不允许重复声明,let不允许在相同作用域内,重复声明同一个变量:
/*-----------const----------*/
1. 声明一个只读的常量。一旦声明,常量的值就不能改变。
2. 一旦声明,就要立即初始化,否则也报错。
3. const命令声明的常量也不提升,同样存在暂时性死区,只能在声明的位置后使用。
4. 也不可以重复声明。

6. ★★★★ 说说你对执行上下文的理解

执行上下文有且只有三类,全局执行上下文,函数上下文,与eval上下文(eval一般不会使用)
1. 全局执行上下文:
   全局执行上下文只有一个,也就是我们熟知的window对象,我们能在全局作用域中通过this直接访问到它
2. 函数执行上下文
   函数执行上下文可存在无数个,每当一个函数被调用时都会创建一个函数上下文;
   需要注意的是,同一个函数被多次调用,都会创建一个新的上下文。

3. 执行上下文栈(下文简称执行栈)也叫调用栈,
执行栈用于存储代码执行期间创建的所有上下文,具有LIFO(Last In First Out后进先出,也就是先进后出)的特性。

JS代码首次运行,都会先创建一个全局执行上下文并压入到执行栈中,之后每当有函数被调用,都会创建一个新的函数执行上下文并压入栈内;由于执行栈LIFO的特性,所以可以理解为,JS代码执行完毕前在执行栈底部永远有个全局执行上下文。

7. ★★★ 对闭包的看法,为什么要用闭包?说一下闭包的原理以及应用场景?闭包的 this 指向问题?

闭包的作用:
1. 在外部访问函数内部的变量
2. 让函数内的局部变量可以一直保存下去
3. 模块化私有属性和公共属性

闭包的原理:
全局变量生存周期是永久,局部变量生存周期随着函数的调用介绍而销毁。
闭包就是 在函数中定义且成为该函数内部返回的函数的自由变量 的变量,该变量不会随着外部函数调用结束而销毁。 
(注:不光是变量,函数内声明的函数也可以形成闭包)
当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。

闭包的应用场景:
// 1. 返回值 最常见的一种形式

    var fun_1 = function () {
      var name = "limo";
      return function () {      
        return name;
      }
    }
    var fun_1 = function () {
      var name = "limo";
      return name;
    }
    var fu_1 = fun_1();
    console.log("fu_1():" + fu_1());

// 2. 函数赋值 一种变形的形式是将内部函数赋值给一个外部变量

    var f2;
    var fun_2 = function () {
      var name = "limop"
      var a = function () {
        return name;
      }
      f2 = a;
    }
    f2();
    console.log(f2);

// 3. 函数参数 通过函数参数引用内部函数产生闭包

    var fn_3 = function (f3) {
      console.log(f3);
    }

    function fun_3() {
      var name = "limo";
      var a = function () {
        return name;
      }
      fn_3(a)
    }
    fun_3();

// 4. IIFE(自执行函数)

    var fn_4 = function (f4) {
      console.log(f4);
    };
    (function fun_4() {
      var name = "limo";
      var a = function () {
        return name;
      }
      fn_3(a)
    })();

// 5. 循环赋值

    function foo(){
      var arr = [];
      for(var i = 0; i < 10; i++){
        arr[i] = (function(n){
          return function(){
            return n;
          }
        })(i)
      }
      return arr;
    }
    var bar = foo();
    console.log(bar[3]());

// 6. getter和setter

    // getter和setter函数来将要操作的变量保存在函数内部,防止暴露在外部
    var getValue, setValue;
    (function () {
      var num = 0
      getValue = function () {
        return num
      }
      setValue = function (v) {
        if (typeof v === 'number') {
          num = v
        }
      }
    })();
    console.log(getValue());    //0
    setValue(10);
    console.log(getValue())     //10

// 7.迭代器(计数器)

    var add = function(){
      var num = 0;
      return function(){
        return ++num;
      }
    }();
    console.log(add());
    console.log(add());

    function setUp(arr){
      var i = 0;
      return function(){
        return arr[i++];
      }
    }
    var next = setUp(['Steve','Alex','LingYi']);
    console.log(next());
    console.log(next());
    console.log(next());

// 8.触发事件

    window.onload = function (){
      var btn = document.querySelector('.btn')
      btn.onclick = function (){//事件相当于在全局触发
        btn.style.color = 'red'//保持对上层作用域的引用 btn
        console.log('abc')
        // this
      }
    }

闭包的this指向问题:

var myNumber = {
  value: 1,
  add: function(i){
    var helper = function(i){
        console.log(this);
          this.value += i;
    }
    helper(i);
  }
}
myNumber.add(1);
1.this指向window对象(因为匿名函数的执行具有全局性,所以其this对象指向window);
2.不能实现value加1(每个函数在被调用时都会自动取得两个特殊变量,this和arguments,内部函数在搜索这两个对象时,只会搜索到其活动对象为止,所以不能实现访问外部函数的this对象);
3.修改代码实现正确功能

第一种解决方法:

var myNumber={
    value:1,
    add:function(i){
        var that=this;//定义变量that用于保存上层函数的this对象
        var helper=function(i){
             console.log(that);
        that.value+=i;
    }
    helper(i);
    }
}
myNumber.add(1);

第二种解决方法:

var myNumber={
    value:1,
    add:function(i){
        var helper=function(i){
            this.value+=i;
        }
        helper.apply(this,[i]);//使用apply改变helper的this对象指向,使其指向myNumber对象
    }
}
myNumber.add(1);
第三种解决方法

var myNumber={
    value:1,
    add:function(i){
        var helper=function(i){
            this.value+=i;
        }.bind(this,i);//使用bind绑定,和apply相似,只是它返回的是对函数的引用,不会立即执行
        helper(i);
    }
}
myNumber.add(1);

8. ★★★ 简述闭包的问题以及优化

闭包的缺点:占用内层空间 大量使用闭包会造成 栈溢出

由于闭包会一直占用内存空间,直到页面销毁,我们可以主动将已使用的闭包销毁:
将闭包函数赋值为null 可以销毁闭包

9. ★★★ 如何确定 this 指向?改变 this 指向的方式有哪些?

 this 指向:
1. 全局上下文(函数外)
无论是否为严格模式,均指向全局对象。注意:严格模式下全局对象为undifined
2. 函数上下文(函数内)
默认的,指向函数的调用对象,且是最直接的调用对象:
简单调用,指向全局对象注意:严格模式下全局对象为undifined,某些浏览器未实现此标准也可能会是window

改变this指向的方式:
1. 第一种: new关键字改变this指向

//构造函数版this
function Fn(){
    this.user = "李某";
}
var a = new Fn();
console.log(a.user); //李某
/*----------------------------------------*/
2. 第二种: call()
// 把b添加到第一个参数的环境中,简单来说,this就会指向那个对象
var a = {
    user:"李某",
    fn:function(){
        console.log(this.user); //李某
    }
}
var b = a.fn;
b.call(a);  //若不用call,则b()执行后this指的是Window对象
/*----------------------------------------*/
3. 第三种:apply()
// apply方法和call方法有些相似,它也可以改变this的指向,也可以有多个参数,但是不同的是,第二个参数必须是一个数组
var a = {
    user:"李某",
    fn:function(){
        console.log(this.user); //李某
    }
}
var b = a.fn;
b.apply(a);
/*----------------------------------------*/
4. 第四种:bind()
// bind方法返回的是一个修改过后的函数,
// bind也可以有多个参数,并且参数可以执行的时候再次添加,但是要注意的是,参数是按照形参的顺序进行的。
var a = {
    user:"李某",
    fn:function(){
        console.log(this.user); //李某
    }
}
var b = a.fn;
var c = b.bind(a);
c();

10. ★★★ 介绍箭头函数的 this

由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值
1. 所以 call() / apply() / bind() 方法对于箭头函数来说只是传入参数,对它的 this 毫无影响。
2. 考虑到 this 是词法层面上的,严格模式中与 this 相关的规则都将被忽略

作为方法的箭头函数this指向全局window对象,而普通函数则指向调用它的对象

11. ★★★ 谈一下你对原型链的理解,画一个经典的原型链图示

原型链:
因为每个对象和原型都有原型,对象的原型指向原型对象,
而父的原型又指向父的父,这种原型层层连接起来的就构成了原型链。

img

12. ★★★ ES5/ES6 的继承除写法以外还有什么区别?

1. ES5 的继承实质上是先创建子类的实例对象,然后再将父类的方法添加 到 this 上(Parent.apply(this)).
2. ES6 的继承机制完全不同,实质上是先创建父类的实例对象 this(所以必 须先调用父类的super()方法),然后再用子类的构造函数修改 this。
3. ES5 的继承时通过原型或构造函数机制来实现。
4. ES6 通过 class 关键字定义类,里面有构造方法,类之间通过 extends 关 键字实现继承。
5. 子类必须在 constructor 方法中调用 super 方法,否则新建实例报错。因 为子类没有自己的 this对象,而是继承了父类的 this 对象,然后对其进行加工。 如果不调用 super 方法,子类得不到 this 对象。
6. 注意 super 关键字指代父类的实例,即父类的 this 对象。 注意:在子类构造函数中,调用 super 后,才可使用 this关键字,否则报错

13. ★★★★ 你对事件循环有了解吗?说说看

Event Loop(事件循环)中,每一次循环称为 tick, 每一次tick的任务如下:

  • 执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行
  • 检查是否存在 Microtask,如果存在则不停的执行,直至清空 microtask 队列
  • 更新render(每一次事件循环,浏览器都可能会去更新渲染)
  • 重复以上步骤

宏任务 > 所有微任务 > 宏任务,如下图所示:

从上图我们可以看出:

1. 将所有任务看成两个队列:执行队列与事件队列。
2. 执行队列是同步的,事件队列是异步的,宏任务放入事件列表,微任务放入执行队列之后,事件队列之前。
3. 当执行完同步代码之后,就会执行位于执行列表之后的微任务,然后再执行事件列表中的宏任务

14. ★★★★ 微任务和宏任务有什么区别?

宏任务(macrotask)微任务(microtask)
谁发起的宿主(Node、浏览器)JS引擎
具体事件1. script (可以理解为外层同步代码) 2. setTimeout/setInterval 3. UI rendering/UI事件 4. postMessage,MessageChannel 5. setImmediate,I/O(Node.js)1. Promise 2. MutaionObserver 3. Object.observe(已废弃;Proxy?对象替代) 4. process.nextTick(Node.js)
谁先运行后运行先运行
会触发新一轮Tick吗不会

15. ★★★★★ 浏览器和 Node 事件循环的区别?

浏览器中的事件循环:

img

?

Node中的事件循环:

Node 中的 Event Loop 和浏览器中的是完全不相同的东西。Node.js 采用 V8 作为 js 的解析引擎,而 I/O 处理方面使用了自己设计的 libuv,libuv 是一个基于事件驱动的跨平台抽象层,封装了不同操作系统一些底层特性,对外提供统一的 API,事件循环机制也是它里面的实现(下文会详细介绍)。

img

?

Node.js 的运行机制如下:

  • V8 引擎解析 JavaScript 脚本。
  • 解析后的代码,调用 Node API。
  • libuv 库负责 Node API 的执行。它将不同的任务分配给不同的线程,形成一个 Event Loop(事件循环),以异步的方式将任务的执行结果返回给 V8 引擎。
  • V8 引擎再将结果返回给用户。

16. ★★★ 异步解决方案有哪些?

解决方案:
/*---------1.回调函数callback:----------*/
被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数。如setTimeOut,ajax请求,readFile等。
例:

function greeting(name) {
  alert('Hello ' + name);
}

function processUserInput(callback) {
  var name = prompt('请输入你的名字。');
  callback(name);
}

processUserInput(greeting);
优点:
解决了异步的问题。

缺点:
回调地狱:多个回调函数嵌套的情况,使代码看起来很混乱,不易于维护。

/*---------2.事件发布订阅:---------*/
当一个任务执行完成后,会发布一个事件,当这个事件有一个或多个‘订阅者’的时候,会接收到这个事件的发布,执行相应的任务,这种模式叫发布订阅模式。如node的events,dom的事件绑定
例:

document.body.addEventListener('click',function(){
  alert('订阅了');
},false);
document.body.click(); 
优点:
时间对象上的解耦。

缺点:
消耗内存,过度使用会使代码难以维护和理解

/*---------3.Promise:---------*/
Promise是es6提出的异步编程的一种解决方案。
Promise 对象有三种状态:

pending: 初始状态,既不是成功,也不是失败状态。
fulfilled: 意味着操作成功完成。
rejected: 意味着操作失败。
promise的状态只能从pending变成fulfilled,和pending变成rejected,状态一旦改变,就不会再改变,且只有异步操作的结果才能改变promise的状态。
例:
let promise = new Promise(function (resolve, reject) {
    fs.readFile('./1.txt', 'utf8', function (err, data) {
        resolve(data)
    })
})

promise
    .then(function (data) {
        console.log(data)
    })
优点:
解决了回调地狱的问题,将异步操作以同步操作的流程表达出来。

缺点:
无法取消promise。如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。当执行多个Promise时,一堆then看起来也很不友好。

/*---------4.Generator:---------*/
Generator是es6提出的另一种异步编程解决方案,需要在函数名之前加一个*号,函数内部使用yield语句。Generaotr函数会返回一个遍历器,可以进行遍历操作执行每个中断点yield。
例:

function * count() {
  yield 1
  yield 2
  return 3
}
var c = count()
console.log(c.next()) // { value: 1, done: false }
console.log(c.next()) // { value: 2, done: false }
console.log(c.next()) // { value: 3, done: true }
console.log(c.next()) // { value: undefined, done: true }
优点:
没有了Promise的一堆then(),异步操作更像同步操作,代码更加清晰。

缺点:
不能自动执行异步操作,需要写多个next()方法,需要配合使用Thunk函数和Co模块才能做到自动执行。


/*---------5.async/await:---------*/
async是es2017引入的异步操作解决方案,可以理解为Generator的语法糖,async等同于Generator和co模块的封装,async 函数返回一个 Promise。
例:

async function read() {
 let readA = await readFile('data/a.txt')
 let readB = await readFile('data/b.txt')
 let readC = await readFile('data/c.txt')

 console.log(readA)
 console.log(readB)
 console.log(readC)
}

read()
优点:
内置执行器,比Generator操作更简单。async/await比*yield语义更清晰。返回值是Promise对象,可以用then指定下一步操作。代码更整洁。可以捕获同步和异步的错误。

17. ★★★ async 和 await 、promise的区别 和 这两个的本质

/*---------Promise概念:---------*/
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,简单地说,Promise好比容器,里面存放着一些未来才会执行完毕(异步)的事件的结果,而这些结果一旦生成是无法改变的

/*---------async await概念:---------*/
async await也是异步编程的一种解决方案,他遵循的是Generator 函数的语法糖,他拥有内置执行器,不需要额外的调用直接会自动执行并输出结果,它返回的是一个Promise对象。

两者的区别:
Promise的出现解决了传统callback函数导致的“地域回调”问题,但它的语法导致了它向纵向发展行成了一个回调链,遇到复杂的业务场景,这样的语法显然也是不美观的。而async await代码看起来会简洁些,使得异步代码看起来像同步代码,await的本质是可以提供等同于”同步效果“的等待异步返回能力的语法糖,只有这一句代码执行完,才会执行下一句。

async await与Promise一样,是非阻塞的。

async await是基于Promise实现的,可以说是改良版的Promise,它不能用于普通的回调函数。

18. ★★★ 简述 aync await 的好处

1. async/await最重要的好处是同步编程风格
2. async/await有本地浏览器支持。截至今天,所有主流浏览器 查看都完全支持异步功能。
3. async关键字。它声明 getBooksByAuthorWithAwait()函数返回值确保是一个 promise,以便调用者可以安全调用 getBooksByAuthorWithAwait().then(...)或 await getBooksByAuthorWithAwait()

19. ★★★ 移动端点击事件 300ms 延迟如何去掉?原因是什么?

300毫秒原因:
当用户第一次点击屏幕后,需要判断用户是否要进行双击操作,于是手机会等待300毫秒,
解决方法:FastClick.js
FastClick 是 FT Labs 专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库。FastClick的实现原理是在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后的click事件阻止掉。

20. ★★★ Cookie 有哪些属性?其中HttpOnly,Secure,Expire分别有什么作用?

Cookie属性:
name  字段为一个cookie的名称。
value  字段为一个cookie的值。
domain  字段为可以访问此cookie的域名。
path  字段为可以访问此cookie的页面路径。 比如domain是abc.com,path是/test,那么只有/test路径下的页面可以读取此cookie。
expires/Max-Age   字段为此cookie超时时间。若设置其值为一个时间,那么当到达此时间后,此cookie失效。不设置的话默认值是Session,意思是cookie会和session一起失效。当浏览器关闭(不是浏览器标签页,而是整个浏览器) 后,此cookie失效。
Size  字段 此cookie大小。
http  字段  cookie的httponly属性。若此属性为true,则只有在http请求头中会带有此cookie的信息,而不能通过document.cookie来访问此cookie。
secure   字段 设置是否只能通过https来传递此条cookie

1 secure属性
当设置为true时,表示创建的 Cookie 会被以安全的形式向服务器传输,也就是只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该信息,所以不会被窃取到Cookie 的具体内容。
2 HttpOnly属性
如果在Cookie中设置了"HttpOnly"属性,那么通过程序(JS脚本、Applet等)将无法读取到Cookie信息,这样能有效的防止XSS攻击。
3 Expire属性
设置Cookie的失效时间:

21. ★★★★ 如何实现函数的柯里化?比如 add(1)(2)(3)

/*-----解决方法1:-----*/
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;
}
console.log(add(1,2)) // 3
console.log(add(1)(2)) // 3
console.log(add(1)(2)(3)) // 6
console.log(add(1,2,3)(4)) // 10

/*-----解决方法2:-----*/

function add () {
    var args = Array.prototype.slice.call(arguments);

    var fn = function () {
        // 把参数都放在一个相当于全局变量的 args 里面 
        args.push(...arguments)
        return fn;
    }

    fn.valueOf = function () {
        return args.reduce(function(a, b) {
            return a + b;
        })
    }

    return fn;
}
console.log(add(1,2)) // 3
console.log(add(1)(2)) // 3
console.log(add(1)(2)(3)) // 6
console.log(add(1,2,3)(4)) // 10

22. ★★★★ 什么是反柯里化

在JavaScript中,当我们调用对象的某个方法时,其实不用去关心该对象原本是否被设计为拥有这个方法,这是动态类型语言的特点。可以通过反柯里化(uncurrying)函数实现,让一个对象去借用一个原本不属于他的方法。

23. ★★ 将 [1,2] 与 [3,[4]] 合并为 [1,2,3,[4]]

JS数组合并方法:
let arr3 = [1,2].concat([3,[4]]);    //[1,2,3,[4]]

24. ★★ Array.forEach() 与 Array.map() 的区别,Array.slice() 与 Array.splice() 的区别?

/*-----forEach-----*/
forEach不支持return,对原来的数组也没有影响。但是我们可以自己通过数组的索引来修改原来的数组

var ary = [12,23,24,42,1];
var res = ary.forEach(function (item,index,input) {
     input[index] = item*10;
})
console.log(res);//-->undefined;
console.log(ary);//-->会对原来的数组产生改变;
/*-----map-----*/
map支持return返回值,也不影响原数组,但是会返回一个新的数组

var ary = [12,23,24,42,1];
var res = ary.map(function (item,index,input) {
     return item*10;
})
console.log(res);//-->[120,230,240,420,10];
console.log(ary);//-->[12,23,24,42,1];

array.slice(start,end)函数是取array数组中指定的一些元素:
根据数组下标start和end,两个参数为取值的开始和结束下标,取出的值不包括end位置的值,生成一个新数组作为返回值;
这里end可以为空,为空则取从start位置到数组结尾的元素,生成新数组。

array.splice(start,length,insert_one......)函数则是直接在原数组进行删除、添加、替换元素的操作:
start为数组删除元素的开始下标,
length为从start位置开始array删除元素的个数,
后面参数为删除之后array重新插入的数据内容,插入位置为删除位置,而非数组开头或末尾,
返回值为array删除的元素组成的数组。
显而易见,splice函数可以用来对数组元素进行替换。由splice操作后的数组array,数组中内容如果已经改变,就再也找不回array在splice之前的模样。

25. ★★ 将 1234567 转换为 1,234,567

function fun(n){
    return String(n).replace(/(?!^)(?=(\d{3})+\.)/g, ",") 
}

26. ★★★ bind 的作用是什么?

bind()方法主要就是将函数绑定到某个对象,
bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()第一个参数的值

27. ★★ Promise.resolve(Promise.resolve(1)).then(console.log) 输出?

// 答案:1

28. ★★★ var let const的区别

1. var声明的变量会挂载在window上,而let和const声明的变量不会
2. var声明变量存在变量提升,let和const不存在变量提升
3. let和const声明形成块作用域
4. 同一作用域下let和const不能声明同名变量,而var可以
5. 使用let/const声明的变量在当前作用域存在暂存死区
6. const一旦声明必须赋值,不能使用null占位,声明后不能再修改,如果声明的是复合类型数据,可以修改其属性

29. ★★★ document load 和 documen ready的区别

DOM文档解析:

解析html结构
加载脚本和样式文件
解析并执行脚本
构造html的DOM模型      //ready
加载图片等外部资源文件
页面加载完毕           //load

document load:
load是当页面所有资源全部加载完成后(包括DOM文档树,css文件,js文件,图片资源等),执行一个函数,load方法就是onload事件。

documen ready:
构造html的DOM模型加载完毕后触发

30. ★★★ 如何自定义事件?

自定义事件
事件是与DOM交互的最常见的方式。通过实现自定义事件,可以让事件用于非DOM代码中。
思想:创建一个管理事件的对象,让其他对象监听那些事件。

基本模式:

function EventTarget(){
    this.handlers = {};
}

EventTarget.prototype = {
    constructor:EventTarget,
    addHandler:function(type,handler){
        if(typeof this.handlers[type] === "undefined"){
            this.handlers[type] = [];
        }
        this.handlers[type].push(handler);
    },
    fire:function(event){
        if(!event.target){
            event.target = this;
        }
        if(this.handlers[event.type] instanceof Array){
            const handlers = this.handlers[event.type];
            handlers.forEach((handler)=>{
                handler(event);
            })
        }
    },
    removeHandler:function(type,handler){
        if(this.handlers[type] instanceof Array){
            const handlers = this.handlers[type];
            for(var i = 0,len = handlers.length; i < len; i++){
                if(handlers[i] === handler){
                    break;
                }
            }
            handlers.splice(i,1);
        }
    }
}

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(),运算结果可以满足如下预期结果

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. ★★★ 如何避免回调地狱?

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

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

2. ES6 co/yield方案
yield: Generator 函数是协程在 ES6 的实现,而yield是 Generator关键字, 异步操作需要暂停的地方,都用yield语句注明。
co: co 模块是著名程序员 TJ Holowaychuk 于 2013 年 6 月发布的一个小工具,用于 Generator 函数的自动执行。

3. 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 的作用

1.callee
callee是对象的一个属性,该属性是一个指针,指向参数arguments对象的函数
作用:就是用来指向当前对象
返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文. 
callee是arguments 的一个属性成员,它表示对函数对象本身的引用,这有利于匿名 
函数的递归或者保证函数的封装性

2.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;
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-22 14:04:22  更:2021-07-22 14:06:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/6 10:54:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码