美文网首页@IT·互联网IT技术文技术文
37道WEB前端开发面试题之JavaScript篇章!

37道WEB前端开发面试题之JavaScript篇章!

作者: 华夏后裔 | 来源:发表于2018-09-07 19:23 被阅读24次

    ajax, 跨域, jsonp

    参考: 《JavaScript》高级程序设计第21章:Ajax和Comet jQuery中Ajax操作

    apply和call的用法和区别:

    用法:

    都能继承另一个对象的方法和属性,区别在于参数列表不一样

    区别:

    Function.apply(obj, args) args是一个数组,作为参数传给Function

    Function.call(obj, arg1, arg2,...) arg*是参数列表

    apply一个妙用: 可以将一个数组默认的转化为一个参数列表

    举个栗子: 有一个数组arr要push进一个新的数组中去, 如果用call的话需要把数组中的元素一个个取出来再push, 而用apply只有Array.prototype.push.apply(this, arr)

    bind函数的兼容性

    用法:

    bind()函数会创建一个新函数, 为绑定函数。当调用这个绑定函数时,绑定函数会以创建它时传入bind方法的第一个参数作为this,传入bind方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数.

    一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

    解释下事件代理

    事件委托利用了事件冒泡, 只指定一个事件处理程序, 就可以管理某一类型的所有事件.

    例: html部分: 要点击li弹出其id

    html部分

    • Li 2
    • Li 3
    • Li 4
    • Li 5
    • Li 6
    • Li 7
    • //js部分

      document.getElementById("list").addHandler("click", function(e){

      var e = e || window.event;

      var target = e.target || e.srcElement;

      if(target.nodeName.toUpperCase == "LI"){

      console.log("List item", e,target.id, "was clicked!");

      }

      });

      解释下js中this是怎么工作的?

      this 在 JavaScript 中主要由以下五种使用场景。

      作为函数调用,this 绑定全局对象,浏览器环境全局对象为 window 。

      内部函数内部函数的 this 也绑定全局对象,应该绑定到其外层函数对应的对象上,这是 JavaScript的缺陷,用that替换。

      作为构造函数使用,this 绑定到新创建的对象。

      作为对象方法使用,this 绑定到该对象。

      使用apply或call调用 this 将会被显式设置为函数调用的第一个参数。

      继承

      参考:js怎么实现继承?

      AMD vs. CommonJS?

      AMD是依赖提前加载

      CMD是依赖延时加载

      什么是哈希表?

      哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

      使用哈希查找有两个步骤:

      使用哈希函数将被查找的键转换为数组的索引。在理想的情况下,不同的键会被转换为不同的索引值,但是在有些情况下我们需要处理多个键被哈希到同一个索引值的情况。

      所以哈希查找的第二个步骤就是处理冲突。处理哈希碰撞冲突。有很多处理哈希碰撞冲突的方法,比如拉链法和线性探测法。

      元素特征转变为数组下标的方法就是散列法。散列法当然不止一种,下面列出三种比较常用的:

      1,除法散列法

      最直观的一种,上图使用的就是这种散列法,公式: index = value % 16

      学过汇编的都知道,求模数其实是通过一个除法运算得到的,所以叫“除法散列法”。

      2,平方散列法

      求index是非常频繁的操作,而乘法的运算要比除法来得省时(对现在的CPU来说,估计我们感觉不出来),所以我们考虑把除法换成乘法和一个位移操作。公式: index = (value * value) >> 28 (右移,除以2^28。记法:左移变大,是乘。右移变小,是除。)

      如果数值分配比较均匀的话这种方法能得到不错的结果,但我上面画的那个图的各个元素的值算出来的index都是0——非常失败。也许你还有个问题,value如果很大,value * value不会溢出吗?答案是会的,但我们这个乘法不关心溢出,因为我们根本不是为了获取相乘结果,而是为了获取index。

      3,斐波那契(Fibonacci)散列法

      解决冲突的方法:

      拉链法

      将大小为M 的数组的每一个元素指向一个条链表,链表中的每一个节点都存储散列值为该索引的键值对,这就是拉链法.

      对采用拉链法的哈希实现的查找分为两步,首先是根据散列值找到等一应的链表,然后沿着链表顺序找到相应的键。

      线性探测法:

      使用数组中的空位解决碰撞冲突

      参考:浅谈算法和数据结构: 十一 哈希表 哈希表的工作原理

      什么是闭包? 闭包有什么作用?

      闭包是指有权访问另一个函数作用域中的变量的函数. 创建闭包常见方式,就是在一个函数内部创建另一个函数.

      作用:

      匿名自执行函数 (function (){ ... })(); 创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在执行完后很快就会被释放,关键是这种机制不会污染全局对象。

      缓存, 可保留函数内部的值

      实现封装

      实现模板

      参考: js闭包的用途

      伪数组:

      什么是伪数组:

      伪数组是能通过Array.prototype.slice 转换为真正的数组的带有length属性的对象

      比如arguments对象,还有像调用getElementsByTagName,document.childNodes之类的,它们都返回NodeList对象都属于伪数组

      我们可以通过Array.prototype.slice.call(fakeArray)将伪数组转变为真正的Array对象: 返回新数组而不会修改原数组

      参考:伪数组

      undefined和null的区别, 还有undeclared:

      null表示没有对象, 即此处不该有此值. 典型用法:

      (1) 作为函数的参数,表示该函数的参数不是对象。

      (2) 作为对象原型链的终点。

      ( 3 ) null可以作为空指针. 只要意在保存对象的值还没有真正保存对象,就应该明确地让该对象保存null值.

      undefined表示缺少值, 即此处应该有值, 但还未定义.

      (1)变量被声明了,但没有赋值时,就等于undefined。

      (2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。

      (3)对象没有赋值的属性,该属性的值为undefined。

      (4)函数没有返回值时,默认返回undefined。

      undeclared即为被污染的命名, 访问没有被声明的变量, 则会抛出异常, 终止执行. 即undeclared是一种语法错误

      参考: undefined与null的区别

      事件冒泡机制:

      从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。如果想阻止事件起泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)来组织事件的冒泡传播。

      解释下为什么接下来这段代码不是 IIFE(立即调用的函数表达式):function foo(){ }();?

      而函数定义(语句以function关键字开始)是不能被立即执行的,这无疑会导致语法的错误(SyntaxError)。当函数定义代码段包裹在括号内,使解析器可以将之识别为函数表达式,然后调用。IIFE: (function foo(){})()

      区分 (function(){})(); 和 (function(){}()); 其实两者实现效果一样。

      函数字面量:首先声明一个函数对象,然后执行它。(function () { alert(1); })();

      优先表达式:由于Javascript执行表达式是从圆括号里面到外面,所以可以用圆括号强制执行声明的函数。(function () { alert(2); }());

      "attribute" 和 "property" 的区别是什么?

      DOM元素的attribute和property两者是不同的东西。attribute翻译为“特性”,property翻译为“属性”。

      attribute是一个特性节点,每个DOM元素都有一个对应的attributes属性来存放所有的attribute节点,attributes是一个类数组的容器,说得准确点就是NameNodeMap,不继承于Array.prototype,不能直接调用Array的方法。attributes的每个数字索引以名值对(name=”value”)的形式存放了一个attribute节点。

      hello

      property就是一个属性,如果把DOM元素看成是一个普通的Object对象,那么property就是一个以名值对(name=”value”)的形式存放在Object中的属性。要添加和删除property和普通的对象类似。

      很多attribute节点还有一个相对应的property属性,比如上面的div元素的id和class既是attribute,也有对应的property,不管使用哪种方法都可以访问和修改。

      总之,attribute节点都是在HTML代码中可见的,而property只是一个普通的名值对属性。

      请指出 document load 和 document ready 两个事件的区别。

      document.ready和onload的区别——JavaScript文档加载完成事件。页面加载完成有两种事件:

      一是ready,表示文档结构已经加载完成(不包含图片等非文字媒体文件)

      二是onload,指示页面包含图片等文件在内的所有元素都加载完成。

      jQuery中$(function(){});他的作用或者意义就是:在DOM加载完成后就可以可以对DOM进行操作。一般情况先一个页面响应加载的顺序是,域名解析-加载html-加载js和css-加载图片等其他信息。

      什么是use strict? 其好处坏处分别是什么?

      在所有的函数 (或者所有最外层函数) 的开始处加入 "use strict"; 指令启动严格模式。

      "严格模式"有两种调用方法

      1)将"use strict"放在脚本文件的第一行,则整个脚本都将以"严格模式"运行。如果这行语句不在第一行,则无效,整个脚本以"正常模式"运行。如果不同模式的代码文件合并成一个文件,这一点需要特别注意。

      2)将整个脚本文件放在一个立即执行的匿名函数之中。

      好处

      消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;

      消除代码运行的一些不安全之处,保证代码运行的安全;

      提高编译器效率,增加运行速度;

      为未来新版本的Javascript做好铺垫。

      坏处

      同样的代码,在"严格模式"中,可能会有不一样的运行结果;一些在"正常模式"下可以运行的语句,在"严格模式"下将不能运行

      浏览器端的js包括哪几个部分?

      核心( ECMAScript) , 文档对象模型(DOM), 浏览器对象模型(BOM)

      DOM包括哪些对象?

      DOM是针对HTML和XML文档的一个API(应用程序编程接口). DOM描绘了一个层次化的节点树, 允许开发人员添加, 移除和修改页面的某一部分.

      常用的DOM方法:

      getElementById(id)

      getElementsByTagName()

      appendChild(node)

      removeChild(node)

      replaceChild()

      insertChild()

      createElement()

      createTextNode()

      getAttribute()

      setAttribute()

      常用的DOM属性

      innerHTML 节点(元素)的文本值

      parentNode 节点(元素)的父节点

      childNodes

      attributes 节点(元素)的属性节点

      参考: HTML DOM 方法

      js有哪些基本类型?

      Undefined, Null, Boolean, Number, String

      Object是复杂数据类型, 其本质是由一组无序的名值对组成的.

      基本类型与引用类型有什么区别?

      基本类型如上题所示. 引用类型则有: Object, Array, Date, RegExp, Function

      存储

      基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中

      引用类型的值是对象, 保存在堆内存中. 包含引用类型的变量实际上包含的并不是对象本身, 而是一个指向改对象的指针

      复制

      从一个变量向另一个变量复制基本类型的值, 会创建这个值的一个副本

      从一个变量向另一个变量复制引用类型的值, 复制的其实是指针, 因此两个变量最终都指向同一个对象

      检测类型

      确定一个值是哪种基本类型可以用typeof操作符,

      而确定一个值是哪种引用类型可以使用instanceof操作符

      关于js的垃圾收集例程

      js是一门具有自动垃圾回收机制的编程语言,开发人员不必关心内存分配和回收问题

      离开作用域的值将被自动标记为可以回收, 因此将在垃圾收集期间被删除

      "标记清除"是目前主流的垃圾收集算法, 这种算法的思路是给当前不使用的值加上标记, 然后再回收其内存

      另一种垃圾收集算法是"引用计数", 这种算法的思想是跟踪记录所有值被引用的次数. js引擎目前都不再使用这种算法, 但在IE中访问非原生JS对象(如DOM元素)时, 这种算法仍然可能会导致问题

      当代码中存在循环引用现象时, "引用计数" 算法就会导致问题

      解除变量的引用不仅有助于消除循环引用现象, 而且对垃圾收集也有好处. 为了确保有效地回收内存, 应该及时解除不再使用的全局对象, 全局对象属性以及循环引用变量的引用

      ES5中, 除了函数,什么能够产生作用域?

      try-catch 和with延长作用域. 因为他们都会创建一个新的变量对象.

      这两个语句都会在作用域链的前端添加一个变量对象. 对with语句来说, 会将指定的对象添加到作用域链中. 对catch语句来说, 会创建一个新的变量对象, 其中包含的是被抛出的错误对象的声明.

      当try代码块中发生错误时,执行过程会跳转到catch语句,然后把异常对象推入一个可变对象并置于作用域的头部。在catch代码块内部,函数的所有局部变量将会被放在第二个作用域链对象中。请注意,一旦catch语句执行完毕,作用域链机会返回到之前的状态。try-catch语句在代码调试和异常处理中非常有用,因此不建议完全避免。你可以通过优化代码来减少catch语句对性能的影响。一个很好的模式是将错误委托给一个函数处理

      with(object) {statement}。它的意思是把object添加到作用域链的顶端

      //代码片段

      function buildUrl(){

      var qs = "?debug=true";

      //with接收location对象, 因此其变量对象中就包含了location对象的所有属性和方法, 而这个变量对象被添加到了作用域链的前端

      with(location){

      //这里的href其实是location.href. 创建了一个名为url的变量, 就成了函数执行环境的一部分

      var url = href + qs;

      }

      return url;

      }

      参考: js try、catch、finally语句还有with语句 JavaScript 开发进阶:理解 JavaScript 作用域和作用域链

      js有几种函数调用方式?

      方法调用模型 var obj = { func : function(){};} obj.func()

      函数调用模式  var func = function(){} func();

      构造器调用模式

      apply/ call调用模式

      描述事件模型?IE的事件模型是怎样的?事件代理是什么?事件代理中怎么定位实际事件产生的目标?

      捕获->处于目标->冒泡,IE应该是只有冒泡没有捕获。

      事件代理就是在父元素上绑定事件来处理,通过event对象的target来定位。

      js动画有哪些实现方法?

      用定时器 setTimeout和setInterval

      还有什么实现动画的方法?

      js动画:

      使用定时器 setTimeout和setInterval

      CSS : transition , animation

      transition 包含4种属性:transition-delaytransition-durationtransition-propertytransition-timing-function,对应动画的4种属性: 延迟、持续时间、对应css属性和缓动函数,

      transform 包含7种属性:animation-nameanimation-durationanimation-timing-functionanimation-delayanimation-directionanimation-iteration-countanimation-fill-modeanimation-play-state,它们可以定义动画名称,持续时间,缓动函数,动画延迟,动画方向,重复次数,填充模式。

      HTML5 动画

      canvas

      svg

      webgl

      参考:前端动画效果实现的简单比较

      面向对象有哪几个特点?

      封装, 继承, 多态

      如何判断属性来自自身对象还是原型链?

      hasOwnPrototype

      ES6新特性

      1) 箭头操作符 inputs=>outputs: 操作符左边是输入的参数,而右边则是进行的操作以及返回的值

      2) 支持类, 引入了class关键字. ES6提供的类实际上就是JS原型模式的包装

      3) 增强的对象字面量.

      可以在对象字面量中定义原型 proto: xxx //设置其原型为xxx,相当于继承xxx

      定义方法可以不用function关键字

      直接调用父类方法

      4) 字符串模板: ES6中允许使用反引号 ` 来创建字符串,此种方法创建的字符串里面可以包含由美元符号加花括号包裹的变量${vraible}。

      5) 自动解析数组或对象中的值。比如若一个函数要返回多个值,常规的做法是返回一个对象,将每个值做为这个对象的属性返回。但在ES6中,利用解构这一特性,可以直接返回一个数组,然后数组中的值会自动被解析到对应接收该值的变量中。

      6) 默认参数值: 现在可以在定义函数的时候指定参数的默认值了,而不用像以前那样通过逻辑或操作符来达到目的了。

      7) 不定参数是在函数中使用命名参数同时接收不定数量的未命名参数。在以前的JavaScript代码中我们可以通过arguments变量来达到这一目的。不定参数的格式是三个句点后跟代表所有不定参数的变量名。比如下面这个例子中,…x代表了所有传入add函数的参数。

      8) 拓展参数则是另一种形式的语法糖,它允许传递数组或者类数组直接做为函数的参数而不用通过apply。

      9) let和const关键字: 可以把let看成var,只是它定义的变量被限定在了特定范围内才能使用,而离开这个范围则无效。const则很直观,用来定义常量,即无法被更改值的变量。

      10) for of值遍历 每次循环它提供的不是序号而是值。

      11) iterator, generator

      12) 模块

      13) Map, Set, WeakMap, WeakSet

      14) Proxy可以监听对象身上发生了什么事情,并在这些事情发生后执行一些相应的操作。一下子让我们对一个对象有了很强的追踪能力,同时在数据绑定方面也很有用处。

      15) Symbols Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的。之后就可以用这个返回值做为对象的键了。Symbol还可以用来创建私有属性,外部无法直接访问由symbol做为键的属性值。

      16) Math, Number, String, Object的新API

      17) Promises是处理异步操作的一种模式

      参考:ES6新特性概览

      如何获取某个DOM节点,节点遍历方式

      获取节点: getElementById() getElementsByTagName()

      节点遍历:先序遍历DOM树的5种方法

      用LESS如何给某些属性加浏览器前缀?

      可以自定义一个函数

      //代码片段

      .border-radius(@values) {

      -webkit-border-radius: @values;

      -moz-border-radius: @values;

      border-radius: @values;

      }

      div {

      .border-radius(10px);

      }

      js异步模式如何实现?

      参考:JavaScript异步编程的Promise模式

      图片预加载的实现

      使用jQuery图片预加载插件Lazy Load

      1.加载jQuery, 与jquery.lazyload.js

      2.设置图片的占位符为data-original, 给图片一个特别的标签,比如class=".lazy"

      3.然后延迟加载: $('img.lazy').lazyload();这个函数可以选择一些参数:

      3.1.图片预先加载距离:threshold,通过设置这个值,在图片未出现在可视区域的顶部距离这个值时加载。

      3.2.事件绑定加载的方式:event

      3.3.图片限定在某个容器内:container

      使用js实现图片加载: 就是new一个图片对象, 绑定onload函数, 赋值url

      用CSS实现图片的预加载

      写一个CSS样式设置一批背景图片,然后将其隐藏

      改进: 使用js来推迟预加载时间, 防止与页面其他内容一起加载

      用Ajax实现预加载

      其实就是通过ajax请求请求图片地址. 还可以用这种方式加载css,js文件等

      如果在同一个元素上绑定了两个click事件, 一个在捕获阶段执行, 一个在冒泡阶段执行. 那么当触发click条件时, 会执行几个事件? 执行顺序是什么?

      我在回答这个题的时候说是两个事件, 先执行捕获的后执行冒泡的. 其实是不对的.

      绑定在目标元素上的事件是按照绑定的顺序执行的!!!!

      即: 绑定在被点击元素的事件是按照代码顺序发生,其他元素通过冒泡或者捕获“感知”的事件,按照W3C的标准,先发生捕获事件,后发生冒泡事件。所有事件的顺序是:其他元素捕获阶段事件 -> 本元素代码顺序事件 -> 其他元素冒泡阶段事件 。

      参考: JavaScript-父子dom同时绑定两个点击事件,一个用捕获,一个用冒泡时执行顺序

      js中怎么实现块级作用域?

      使用匿名函数, (立即执行函数)

      (function(){...})()

      使用es6

      块级作用域引入了两种新的声明形式,可以用它们定义一个只存在于某个语句块中的变量或常量.这两种新的声明关键字为:

      let: 语法上非常类似于var, 但定义的变量只存在于当前的语句块中

      const: 和let类似,但声明的是一个只读的常量

      使用let代替var可以更容易的定义一个只在某个语句块中存在的局部变量,而不用担心它和函数体中其他部分的同名变量有冲突.在let语句内部用var声明的变量和在let语句外部用var声明的变量没什么差别,它们都拥有函数作用域,而不是块级作用域.

      构造函数里定义function和使用prototype.func的区别?

      直接调用function,每一个类的实例都会拷贝这个函数,弊端就是浪费内存(如上)。prototype方式定义的方式,函数不会拷贝到每一个实例中,所有的实例共享prototype中的定义,节省了内存。

      但是如果prototype的属性是对象的话,所有实例也会共享一个对象(这里问的是函数应该不会出现这个情况),如果其中一个实例改变了对象的值,则所有实例的值都会被改变。同理的话,如果使用prototype调用的函数,一旦改变,所有实例的方法都会改变。——不可以对实例使用prototype属性,只能对类和函数用。

      js实现对象的深克隆

      因为js中数据类型分为基本数据类型(number, string, boolean, null, undefined)和引用类型值(对象, 数组, 函数). 这两类对象在复制克隆的时候是有很大区别的. 原始类型存储的是对象的实际数据, 而对象类型存储的是对象的引用地址(对象的实际内容单独存放, 为了减少数据开销通常放在内存中). 此外, 对象的原型也是引用对象, 它把原型的属性和方法放在内存中, 通过原型链的方式来指向这个内存地址.

      于是克隆也会分为两类:

      浅度克隆:

      原始类型为值传递, 对象类型仍为引用传递

      深度克隆:

      所有元素或属性均完全复制, 与原对象完全脱离, 也就是说所有对于新对象的修改都不会反映到原对象中

      深度克隆实现:

      //代码片段

      function clone(obj){

      if(typeof(obj)== 'object'){

      var result = obj instanceof Array ? [] : {};

      for(var i in obj){

      var attr = obj[i];

      result[i] = arguments.callee(attr);

      }

      return result;

      } else {

      return obj;

      }

      };

      参考: JavaScript深克隆 javascript中对象的深度克隆

      相关文章

        网友评论

          本文标题:37道WEB前端开发面试题之JavaScript篇章!

          本文链接:https://www.haomeiwen.com/subject/xychgftx.html