2020年最新最全的前端面试题整理----原生JS篇

前言

 五一结束了,假期总是过得那么快,不知道前两篇的内容大家记住多少了呢?五一后找工作的小伙伴们评论区举爪组队咯~

原生JS篇

JS是一种什么样的语言?

  1. 解释性脚本语言,代码不进行预编译
  2. 主要用来向HTML页面添加交互行为
  3. 可以直接嵌入HTML页面,但单独写成JS文件有利于结构和行为的分离
  4. 跨平台性,在绝大多数浏览器的支持下,可以在多种平台下运行:linux、windows

JS数据类型有哪些?

栈: (原始数据) string/number/boolean/null/undefined/symbol
堆: (引用数据类型)object(array和函数属于object)
数据类型一共7(6种基本类型+1种引用类型)种

介绍JS有哪些内置对象?

object是Javascript中所有对象的父对象
数据封装类对象:Object、Array、Boolean、Number和String
其他对象:Function、Arguments、Math、Date、RegExp、Error

栈与堆的区别?

栈与堆的储存位置不同;
原始数据是储存在栈中简单数据段,体积小,大小固定,属于频繁使用的数据.
引用数据类型是储存在堆中的对象,占据的空间大,如果储存在栈中会影响运行性能,引用数据类型在栈中指明了自己的所在地。当代码解析时,会先从栈中获取地址,然后再从堆中获取实体;

js中的作用域,变量声明与提升

**作用域:**每一个变量、函数都有其作用的范围,超出范围不得使用,这个叫做作用域
全局变量、局部变量:
全局变量:在全局范围内声明的变量,如var a =1;
只有赋值没有声明的值,如a =1(注:如果a=2在函数环境中,也是全局变量)
局部变量:写入函数的变量,叫做局部变量,作用范围仅限函数内部
作用:程序安全,内存的释放
作用域链:
查找变量的过程。先找自己局部环境内部有没有声明或者是函数,如果有,则查看声明有无赋值或者是函数的内容,如果没有,则向上一级查找。
变量声明提升:
在预编译阶段,编译器会把所有定义的变量全部提升到最顶部,即,把变量声明的语句会自动放置在最顶部。

console.log(a)何时会打印1?

当函数内部没有a这个变量的时候,才会向上一级查找

如何转化类型?

转数组parseFloat();
转字符串toString()/string()
数组转字符串 join();
字符串转数组: split();

什么是面向对象编程及面向过程编程,他们的异同和优缺点

面向过程就是分析出解决问题所需要的步骤,然后用函数吧这些步骤一步一步实现,使用的时候一个一个一次调用就可以了
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个实物在整个解决问题的步骤中的行为面向对象是以功能来划分问题,而不是步骤面向对象编程思想基本思想是使用对象、类、继承、封装等基本概念来进行程序设计
优点:易维护

  • 采用面向对象思想设计的结构,可读性高,由于继承的存在,即使改变需求,那么维护起来是非常方便你和较低成本的
    易扩展
    开发工作的重用性、继承性高、降低重复工作量,缩短了开发周期

如何解释this在js中起的作用?

Js中的this,一般取决于调用这个函数的方法
1、如果函数被实例化(new 构造函数名())的情况下,this指向全新的对象
2、如果是某标签触发什么事件,调用了这个函数,this指向标签(整个DOM节点,包含它的子元素);
3、如果函数使用了call/apply,this是作为参数传入对象
4、有时候this指向不明确的话,this会指向window,ES6中的箭头函数修改了this指向,永远指向作用域
js中this的用法(经典):
this是js的关键字,随着函数使用场合不同,this的值会发生变化。但是总有一个原则,那就是this指的是调用函数的那个对象。
//纯粹的函数调用,this指向全局

function test(){
// this.x = 1;
console.log(this);
}
test();
 

//作为方法调用,那么this就是指向这个上级对象

function test1(){
console.log(this)
}
var obj = {}
obj.x = test1;
obj.x()
 

//构造函数调用,就是生成一个新的对象,这里的this指向这个对象

function test2(){
console.log(this)
}
var m = new test2();
 

//apply调用
//this指向的事apply中的第一个参数

var x = 0;
function test3(){
console.log(this.x);
}
var o = {};
o.x = 1;
o.m =test3;
o.m.apply(); //0
o.m.apply(o); //1

☆说说JS原型和原型链

原型:函数都要prototype(显示原型)属性,而prototype会自动初始化一个空对象,这个对象就是原型对象
原型对象中会有一个constructor属性,这个属性将指向了函数本身
实例化对象都有一个_proto_(隐式原型)属性,_proto_属性指向原型对象
原型链:从实例对象往上找构造这个实例的相关对象,然后这个关联的对象再往上找,找到创造它的上一级的原型对象,以此类推,一直到object.prototype原型对象终止,原型链结束.
原型链中的原型对象中的内容,是会被不同的实例,所共有的

如何准确判断一个变量是数组类型?

instanceof用于判断引用类型属于哪个构造函数的方法

var arr = [];
arr  instanceof  Array;   //true
typeof  arr;    //object    typeof是无法判断是否为数组的

原理:
instanceof是用来判断实例的_proto_和构造函数的prototype是否指向一个原型对象,
但是有一个弊端,只要出现在一条原型链上的,都会返回true(每个函数都有prototype,每个对象都有一个内部属性__proto__,其指向它的原型对象。原型对象也是一个对象,所以也有__proto__)
这个时候要用实例__proto__.constructor更加严谨

var arr = [ ];
console.log(arr instanseof Array);   //true
console.log(arr.__proto__.constructor === Array)   //true

☆call和apply的区别和作用?

apply和call都是调用一个对象的一个方法,用另一个对象替换当前对象。
相同点:方法的含义是一样的,即方法功能是一样的。并且第一个参数的作用是一样的
不同点:call可以传入多个参数、apply只能传入两个参数,所以其第二个参数往往是作为数组形式传入
存在意义:实现(多重)继承

继承的方法有哪些?

原型链继承、构造继承、实例继承、拷贝继承、组合继承、寄生组合继承
继承详情解释:
既然要实现继承,那么我们首先要有一个父类,代码如下:

//先定义一个父类
function Animal(name){
//属性
this.name = name || 'Animal';
//实例方法
this.sleep = function(){
console.log(this.name + "正在睡觉!")
}
}
//原型方法
Animal.prototype.eat = function(food){
console.log(this.name + "正在吃" + food);
}

原型链继承
核心:将父类的实例作为子类的原型

//原型链继承
function Cat(){ }
Cat.prototype = new Animal();
Cat.prototype.name = "cat";
 
//Test Code
var cat = new Cat();
console.log(cat.name); //cat
console.log(cat.eat("fish")); //cat正在吃fish
console.log(cat.sleep()); //cat正在睡觉
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true

特点:
1、非常纯粹的继承关系,实例是子类的实例,也是父类的实例
2、父类新增原型方法、原型属性,子类都能够访问到
3、简单,易于实现
缺点:

  1. 要想实现子类新增属性的方法,必须要在new Animal( )这样的语句之后执行,补鞥呢放在构造器中
  2. 无法实现多继承
  3. 来自原型对象的引用属性是所有实例共享的
  4. 创建子类实例时, 无法向父类构造函数传参
    构造函数
    核心:使用父类的构造函数来增强子类实例,等于是赋值父类的实例属性给子类(没用的原型)
//构造函数
function Cat(name){
Animal.call(this);
this.name = name || "Tom"
}
//Test Code
var cat = new Cat();
console.log(cat.name); //Tom
console.log(cat.sleep()); //Tom正在睡觉
console.log(cat instanceof Animal); //false
console.log(cat instanceof Cat); //true

特点:

  1. 解决了1中,子类实例共享父类引用属性的问题
  2. 创建子类实例时,可以向父类传递参数
  3. 可以实现多继承(call多个父类对象)
    缺点:
  4. 实例并不是父类的实例,只是子类的实例
  5. 只能继承父类的实例属性与方法,不能继承原型属性、方法
  6. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
    实例继承
    核心: 为父类实例添加新特性,作为子类实例返回
//实例继承
function Cat(name){
var instance = new Animal();
instance.name = name || "Tom";
return instance;
}
//Test Code
var cat = new Cat();
console.log(cat.name); //Tom
console.log(cat.sleep()); //Tom正在睡觉!
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //false

特点:

  1. 不限制调用方法,不管是new子类()还是子类(),返回的对象具有相同的效果
    缺点:
  2. 实例是父类的实例, 不是子类的实例
  3. 不支持多继承
    拷贝继承
//拷贝继承
function Cat(name){
var animal = new Animal();
for(var p in animal){
Cat.prototype[p] = animal[p];
}
Cat.prototype.name = name || "Tom"
}
//Test Code
var cat = new Cat();
console.log(cat.name); //Tom
console.log(cat.sleep()); //Tom正在睡觉!
console.log(cat instanceof Animal); //false
console.log(cat instanceof Cat); //true

特点:

  1. 支持多继承
    缺点:
  2. 效率较低,内存占用高(因为要拷贝父类的属性)
  3. 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
    组合继承
    核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
//组合继承
function Cat(name){
Animal.call(this);
this.name = name || "Tom";
}
Cat.prototype = new Animal();
//组合继承也需要修复构造函数的指向问题
Cat.prototype.constructor = Cat;
//Test Code
var cat = new Cat();
console.log(cat.name); //Tom
console.log(cat.sleep()); //Tom正在睡觉!
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true

特点:

  1. 弥补了方法2的缺陷,可以继承实例属性、方法,也可以继承原型属性和方法
  2. 既是子类的实例,也是父类的实例
  3. 不存在引用属性共享的问题
  4. 可传参
  5. 函数可复用
    缺点:
  6. 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
    寄生组合继承
    核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法、属性,避免了组合继承的缺点
//寄生组合继承
function Cat(name){
Animal.call(this);
this.name = name || "Tom"
}
(function(){
//创建一个没有实例方法的类
var Super = function(){};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
})();
//Test Code
var cat = new Cat();
console.log(cat.name); //Tom
console.log(cat.sleep()); //Tom正在睡觉!
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
//该实现没有修复constructoe

特点:

  1. 堪称完美
    缺点:
  2. 实现较为复杂

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

由于在js中,变量到的作用域属于函数作用域,在函数执行后作用域会被清除、内存也会随之被回收,但是由于闭包是建立在一个函数内部的子函数,由于其可访问上级作用域的原因,即使上级函数执行完,作用域也不会随之销毁,这时的子函数—也就是闭包,便拥有了访问上级作用域中的变量权限,即使上级函数执行完后,作用域内的值也不会被销毁。
闭包解决了什么:在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
由于闭包可以缓存上级作用域,那么就使得函数外部打破了“函数作用域”的束缚,可以访问函数内部的变量。以平时使用的Ajax成功回调为例,这里其实就是个闭包,由于上述的特性,回调就拥有了整个上级作用域的访问和操作能力,提高了几大的便利。开发者不用去写钩子函数来操作审计函数作用域内部的变量了。
闭包有哪些应用场景:
闭包随处可见,一个Ajax请求的成功回调,一个事件绑定的回调函数,一个setTimeout的延时回调,或者一个函数内部返回另一个匿名函数,这些都是闭包。简而言之,无论使用何种方式对函数类型的值进行传递,当函数在别处被调用时都有闭包的身影
闭包的缺陷:由于闭包打破了函数作用域的束缚,导致里面的数据无法清除销毁,当数据过大时会导致数据溢出
事件代理(事件委托):
事件代理是将子元素的事件写一个父元素,让父元素代替处理,内部使用e.target,e.target就是触发这个事件的子元素事件的各个阶段
捕获阶段 —> 目标阶段 —> 冒泡阶段
document —> target目标 —> document
由此addEventListener的第三个参数设置为true和false的区别已经非常清晰了
true—>代表该元素在事件的”捕获阶段”(由外向内传递)响应事件
false —>表示该元素在事件的”冒泡阶段”(由内向外传递)响应事件

☆new操作符在创建实例的时候经历了哪几个阶段

new创建了一个对象,共经历了4个阶段:
1、 创建一个空对象
2、 设置原型链
3、让实例化对象中的this指向对象,并执行函数体
4、 判断实例化对象的返回值类型

异步编程的实现方式

回调函数
优点:简单、容易理解
缺点:不利于维护,代码耦合高
-事件监听(采用时间驱动模式,取决于某个事件是否发生)
优点:容易理解,可以绑定多个事件,每个时间可以指定多个回调函数
缺点:事件驱动型,流程不够清晰
-发布、订阅(观察者模式)
类似于事件监听,但是可以通过‘消息中心’,了解现在有多少发布者,多少订阅者
Promise对象
优点:可以利用then方法,进行链式写法;可以书写错误时的回调函数;
缺点:编写和理解,相对比较难
Generator函数
优点:函数体内外的数据交换、错误处理机制
缺点:流程管理不方便
async函数
优点:内置执行器、更好的语义、更广的适用性、返回的是Promise,结构清晰
缺点:错误处理机制

对原生JS了解程度

数据类型、运算、对象、Function、继承、闭包、作用域、原型链、事件、RegExp、JSON、Ajax、DOM、BOM、内存泄漏、跨域、异步装载、模板引擎、前端MVC、MVVM、路由、模块化、Canvas、ECMAScript

js延迟加载的方法有哪些?

defer和async、动态创建DOM方式(用的最多),按需异步载入JS
defer属性:(页面load后执行)
script标签定义了defer属性
用途:表明脚本在执行时不会影响页面的构造。也就是所,脚本会被延迟到整个页面解析完毕之后再执行。

<script  src=XXX.js” defer=“defer”></script>

async属性:(页面load前执行)
script标签定义了async属性。与defer属性类似,都用于改变处理脚本的行为。同样,只适用于外部脚本文件。
目的:不让页面等待脚本下载和执行,从而异步加载页面其他内容。异步脚本一定会在页面load事件前执行。不能保证脚本会按顺序执行

动态创建DOM方式:

1、document.write(); 不常用,因为容易覆盖原来的页面。

2、innerHTML = (); 用的比较多。绑定属性和内容比较方便。(节点套节点)

3、document.createElement(); 用得也比较多,指定数量的时候一般用它。

数组从小到大排序?

方法一: sort方法

var array = [1, 4, -8, -3, 6, 12, 9, 8];
            function compare(val1, val2) {
                return val1 - val2;
            };
            array.sort(compare);
            document.write(array);
方法二:冒泡排序
var array = [1, 4, -8, -3, 12, 9];
       function sort(arr) {
           for(var i = 0;i < arr.length;i++){
                ////两两比较,如果前一个比后一个大,则交换位置
                for(var j = i + 1; j < arr.length; j++) {
                    if(arr[i] > arr[j]) {
                         var temp = arr[i];
                         arr[i] = arr[j];
                         arr[j] = temp;
                    }
                }
           }
       }
      sort(array);
      console.log(array)

查看其他排序方式可以看:
https://www.cnblogs.com/real-me/p/7103375.html
求从大到小排序可以先使数组从大到小排序,然后添加reverse()方法,使数组顺序颠倒

为string扩展一个trim方法,取掉字符串中的所有空格

方法一:trim()方法------仅能取掉字符串首尾空格

var str = " a b c "
console.log(“trim”,str.trim());
//trim原理
function Trim(str){
return str.replace(/(^\s*)|(\s*$)/g, “”);
}

方法二:去除字符中所有的空格

str.replace(/\s/ig,'')

如何实现数组的随机排序?

最快是给数组添加原生sort()方法,可以随机数组,如果sort(),方法没有参数的话,就是依照数据的unicode ['junɪˌkod]码排序的
可以在sort()中添加一个比较函数函数:

  function(a,b){  return Math.rendom()>.5? -1:1  }

Math.rendom()在0到1之间生成一个随机数

图片懒加载

图片懒加载理解:由于商城图片过多时,就会给图片加一个懒加载的缓冲效果。当图片进入可视化区域的时候才会加载,否则图片只是一个空标签。这样可以优化页面渲染速度,提升用户体验。
思路:将页面中的所有img属性src用data-src代替,当页面滚动至此图片出现在可视区域时,用js取到该图片的data-src值赋给src。

所用知识点:
浏览器可视区域的宽高:
js : document.body.clientWidth/clientHeight
jquery: var windHeight = ( w i n d o w ) . w i d t h ( ) / (window).width()/ (window).width()/(window).height();
获取滚动条相对于顶部的高度:
js : document.body.scrollTop;
jquery : var scrollTop= ( w i n d o w ) . s c r o l l T o p ; 获 得 元 素 对 于 浏 览 器 顶 部 的 高 度 : j s : D O M 元 素 . o f f s e t T o p ; j q u e r y : v a r i m g T o p = (window).scrollTop; 获得元素对于浏览器顶部的高度: js : DOM元素.offsetTop; jquery: var imgTop= (window).scrollTop;:js:DOM.offsetTop;jquery:varimgTop=(‘img’).offset().top
判断元素是否出现在浏览器的可视化区域内:
元素相对于顶部的高度 - 浏览器可视化区域的高度 < 小于滚动条到顶部的高度
成立就代表出现 : 不成立就没出现
怎样排除首屏的图片
元素到顶部距离 - 浏览器的可视化高度 > 0
排除已加载的图片
$(this).attr(‘src’) != $(this).attr(‘data-src’) //排除已加载的图片
Jquery实现图片懒加载 :

<script>
      // 注意: 需要引入jQuery和underscore
      $(function() {
          // 获取window的引用:
          var $window = $(window);
          // 获取包含data-src属性的img,并以jQuery对象存入数组:
          var lazyImgs = _.map($('img[data-src]').get(), function (i) {
              return $(i);
          });
          // 定义事件函数:
          var onScroll = function() {
              // 获取页面滚动的高度:
              var wtop = $window.scrollTop();
              // 判断是否还有未加载的img:
              if (lazyImgs.length > 0) {
                  // 获取可视区域高度:
                  var wheight = $window.height();
                  // 存放待删除的索引:
                  var loadedIndex = [];
                  // 循环处理数组的每个img元素:
                  _.each(lazyImgs, function ($i, index) {
                      // 判断是否在可视范围内:
                      if ($i.offset().top - wtop < wheight) {
                          // 设置src属性:
                          $i.attr('src', $i.attr('data-src'));
                          // 添加到待删除数组:
                          loadedIndex.unshift(index);
                      }
                  });
                  // 删除已处理的对象:
                  _.each(loadedIndex, function (index) {
                      lazyImgs.splice(index, 1);
                  });
              }
          };
          // 绑定事件:
          $window.scroll(onScroll);
          // 手动触发一次:
          onScroll();
    </script>

onScroll()函数最后要手动触发一次,因为页面显示时,并未触发scroll事件。如果图片已经在可视化区域内,这些图片仍然是loading状态,需要手动触发一次,就可以正常显示。

js中常见的内存泄漏

  1. 内存泄漏会导致一系列问题,比如:运行缓慢、崩溃、高延迟
  2. 内存泄漏是指你用不到(访问不到)的变量,依然占据着内存空间,不能被再次利用起来
  3. 意外的全局变量,这些都是不会被回收的变量(除非设置null或者被重新赋值),特别是那些用来临时存储大量信息的变量
  4. 周期函数一直在运行,处理函数并不会被回收,jq在移除节点前都会,将事件监听移除
  5. js代码中有对DOM节点的引用,dom节点被移除的时候,引用还维持

深拷贝和浅拷贝的问题:

  1. 深拷贝和浅拷贝值针对Object和Array这样的复杂类型
  2. a和b指向了同一块内存,所以修改其中任意一个值,另外一个值也会随之变化,这是浅拷贝
  3. a和b指向同一块内存,但是修改其中任意一个值,另外一个调用的变量,不会受到影响,这是深拷贝
  4. 浅拷贝:“Object.assign()”方法用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象,它将返回目标对象
  5. 深拷贝:JSON.parse( )和JSON.stringify( )给了我们一个基本的解决办法。但是函数不能被正确处理

显示转换与隐式转换

JS中有5中简单的数据类型(也称之为基本数据类型):undefined、Null、Boolean、Number、String。还有一种复杂的数据--------Object,Object本质是一组无序的名值对组成的。
对一个值使用typeof操作符可以返回该值的数据类型,typeof操作符会返回一些令人迷惑但技术上却正确的值,比如调用typeof null会返回“object”,应为特殊值null被认为是一个空的对象引用。
**显式转换:**主要通过JS定义的数据转换方法
各种数据类型及其对应的转化规则:
数据类型 转换为true的值 转换为false的值
Boolean true false
String 任何非空字符串 “” (空字符串)
Number 任何非零数字值(包括无穷大) 0和NaN
Object 任何对象 null
Underfined n/a undefined
**隐式转换:**是系统默认的,不需要加以声明就可以进行的转换。一般情况下,数据的类型转换通常是由编译系统自动进行的,不需要人工干预
大致规则如下:
1、 对象和布尔值比较
对象和布尔值比较时,对象先转换为字符串,然后再转换为数字,布尔值直接转换为数字
2、对象和字符串比较
对象和字符串进行比较时,对象转换为字符串,然后两者进行比较
3、对象和数字比较
对象和数字进行比较时,字符串转换为数字,二者再比较
4、字符串和数字比较
字符串和数字进行比较时,字符串转换成数字,二者再比较,true=1,false=0
5、字符串和布尔值比较
字符串和布尔值进行比较时,二者全部转换成数值再比较
6、布尔值和数字比较
布尔值和数字进行比较时,布尔转换为数字,二者比较

父元素和子元素分别有点击事件的情况下:

点击父元素只会触发父元素事件,不会影响到子元素,如果点击子元素,会因为冒泡触发父元素的点击事件,可是阻止默认冒泡事件;

mouseover/mouseout与mouseenter/mouseleave的区别与联系

1、mouseover/mouseout是标准事件,所有浏览器都支持;mouseenter/mouseleave是IE5.5引入的特有事件,后来被DOM3标准采纳,现代浏览器也支持
2、mouseover/mouseout是冒泡事件;mouseenter/mouseleave不冒泡.需要为多个元素监听鼠标移入/移出事件时,推荐使用mouseover、mouseout托管,提高性能

ForEach和map的区别在哪里:

这两个API都可以遍历数组
forEach函数,是给数组中的每个都执行一遍回调函数,不会返回一个值

Map方法是通过调用数组中的每个元素,映射到新元素中,从而床架一个新数组
如果是复合型类型时,如果只改变复合类型的其中某个value时,将可以正常使用
JS判断设备来源

function deviceType(){
        var ua = navigator.userAgent;
        var agent = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];    
        for(var i=0; i<len,len = agent.length; i++){
            if(ua.indexOf(agent[i])>0){         
                break;
            }
        }
    }
    deviceType();
    window.addEventListener('resize', function(){
        deviceType();
    })

微信的 有些不太一样

   function isWeixin(){
        var ua = navigator.userAgent.toLowerCase();
        if(ua.match(/MicroMessenger/i)=='micromessenger'){
            return true;
        }else{
            return false;
        }
    }

DOM元素的e的e.getAttribute(propName)和e.propName有什么区别和联系?

e.getAttribute()是标准DOM操作文档元素属性的方法,具有通用性可在任意文档上使用,返回元素在源文件中设置的属性
e.propName通常是在HTML文档中访问特定元素的特性,浏览器解析元素后生产对应对象,这些对象的特性会根据特定规则结合属性设置得到,对于没有对应特性的属性,只能使用getAttribute进行访问
e.getAttribute()返回值是源文件中设置的值,类型是字符串或者是null
e.propName返回值可能是字符串、布尔值、对象、undefined等
大部分attribute与property是一一对应关系,修改其中一个会影响另外一个,如id、title等属性
一些布尔属性的检测设置需要hasAttribute和removeAttribute来完成,或者设置对应的property
像link中的href属性,转换成property的时候需要通过转换得到完整的url
一些attribute和property不是一一对应,如:form控件中对应的是defaultValue,修改或设置value property修改的是控件当前值,setattribute修改value属性不会改变value property

offsetWidth/offsetHeight、clientWidth/clientHeight与scrollWidth/scrollHeight的区别?

offfsetWidth、offsetHeight返回值包含content+padding+border,效果与e.getBoundingClientRect()相同
clientWidth、clientHieight返回值值包含content+padding,如果有滚动条,也不包含滚动条
scrollWidth、scrollHeight返回值包含content+padding+溢出内容的尺寸

focus/blur与focusin/focusout的区别和联系

1、 focus/blur不冒泡,focusin/focusout冒泡
2、 focus/blur兼容性好,focusin、focusout在除fireFox外的浏览器下都保持良好兼容性,如需使用事件托管,可考虑FireFox下使用事件捕获
elem.addEventListener(‘focus’,handler,true)
3. 可获得焦点的元素:
window/链接被点击或键盘操作/表单控件被点击或键盘操作/设置tabindex属性的元素被点击或键盘操作

=的区别?

===为等同符,当左边与右边的值与类型都完全相等时,会返回true;
==为等值符,用来判断值是否相同,不会判断类型是否相同
addEventListener监听点击事件与click事件有什么区别?
addEventListener事件可以对普通元素进行多个事件处理,click事件只能使元素运行最新的事件结果

null和undefined的区别?

null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在对象
undefined主要指定义了变量,但是并未赋值
NAN (not a Number)不是一个明确数值的数字类型
ECMAScript认为undefined是从null派生出来的,他们的值是一样的,但是类型却不一样。
所以
null == undefined //true
null === undefined //false

如何检查对象中是否存在某个属性?

检查对象中存在某个属性的方式有三种:
第一种使用in操作符号, 返回true或false:
console.log(“属性名” in 对象名);
第二种使用hasOwnProperty方法, 会返回true或false:
console.log(对象.hasOwnProperty(“属性名”));
第三种是是够括号符号obj[‘属性名’], 如果属性存在, 则返回该属性的值, 如果不存在, 则返回undefined

字符串操作方法。

splite():用于把一个字符串分割成字符串数组
search():用于减缩字符串中指定的子字符串,或检索与正则表达式相匹配的字符串
indexOf():可返回某个字符换字符串中首次出现的位置
substring():用于提取字符串中介于两个指定下标之间的字符
trim():移除字符串两边的空格
replace():替换字符

数组操作方法。

length:计算数组的长度
索引:通过索引获取数组中对应值,同时也可以改变索引对应的值
indexOf:返回指定元素的位置,若元素不存在返回-1
slice:接受1-2个参数,参数对应的是要返回的是要返回项的起始位置和结束位置,若只有一个参数,该方法返回从参数指定位置到数组结尾的所有项,如果还有两个参数,则返回起始位置到结束位置项,但是不包括结束位置项,返回的结果是一个新数组。
push:向数组末位添加若干元素,返回添加元素后数组的长度
pop:删除数组末位最后一个元素,但会被删除元素
unshift:向数组头部添加若干元素,返回添加元素后的数组长度
shift:删除数组头部的第一个元素,返回被删除的元素
sort:对数组进行排序,返回排序后的数组
reverse:对数组中的数据进行反转,返回反转后的数组
concat:将两个数组合并,返回新数组(可以接受任意元素和数组,并进行拆分放入数组)
join:将数组中的每一个元素用指定的字符串拼接起来
js的 for 跟for in 循环它们之间的区别?
遍历数组时的异同: for循环 数组下标的typeof类型:number, for in 循环数组下标的typeof类型:string;

var southSu = ['苏南','深圳','18','男'];for(var i=0;i<southSu.length;i++){
    console.log(typeof i); //number
    console.log(southSu[i]);// 苏南 , 深圳 , 18 , 男
}var arr = ['苏南','深圳','18','男','帅气',"@IT菲酵犯缌?-首席填坑官"];for(var k in arr){
    console.log(typeof k);//string
    console.log(arr[k]);// 苏南 , 深圳 , 18 , 男 , 帅气,平头哥联盟-首席填坑官
}

遍历对象时的异同:for循环 无法用于循环对象,获取不到obj.length; for in 循环遍历对象的属性时,原型链上的所有属性都将被访问,解决方案:使用hasOwnProperty方法过滤或Object.keys会返回自身可枚举属性组成的数组

Object.prototype.test = '原型链上的属性,本文由平头哥联盟-首席填坑官∙苏南分享';var southSu = {name:'苏南',address:'深圳',age:18,sex:'男',height:176};for(var i=0;i<southSu.length;i++){
    console.log(typeof i); //空
    console.log(southSu[i]);//空
}
 
for(var k in southSu){
    console.log(typeof k);//string
    console.log(southSu[k]);// 苏南 , 深圳 , 18 , 男 , 176 ,本文由平头哥联盟-首席填坑官∙苏南分享
}

push()、pop()、shift()、unshift()分别是什么功能?

push 方法 将新元素添加到一个数组中,并返回数组的新长度值。
var a=[1,2,3,4]; a.push(5);
pop 方法 移除数组中的最后一个元素并返回该元素。
var a=[1,2,3,4]; a.pop();
shift 方法 移除数组中的第一个元素并返回该元素。
var a=[1,2]; alert(a.shift());
unshift 方法 将指定的元素插入数组开始位置并返回该数组。

如果用原生js给一个按钮绑定两个click事件?

使用事件监听,可给一个DOM节点绑定多个事件(addEventListener)

拖拽会用到哪些事件?

dragstart—拖拽开始时在被拖拽元素上触发此事件,监听器需要设置拖拽所需数据,操作系统拖拽文件到浏览器时不触发此事件
dragenter—拖拽鼠标进入元素时在该元素上触发,用于给拖放的元素设置视觉反馈,如高亮
dragover—拖拽时鼠标在目标元素上移动时触发,监听器通过组织浏览器默认行为设置元素为可拖放元素
dragleave—拖拽时鼠标移出目标元素时在目标元素上触发,此时监听器可以取消掉前面设置的视觉效果
drag—拖拽期间在被拖拽元素上连续触发
drop—鼠标在拖放目标上释放时,在拖放目标上触发,此时监听器需要收集数据并且执行所需操作,如果是从操作系统拖放文件到浏览器,需要取消浏览器默认行为
dragend—鼠标在拖放目标上释放时,在拖拽元素上触发,将元素从浏览器拖放到操作系统时不会触发此事件

JS中定时器有哪些?他们的区别及用法是什么?

setTimeout 只执行一次
setInterval 会一直重复执行

document.write和innerHTML的区别?

document.write是直接写入到页面的内容流,如果在写之前没有调用document.open,浏览器会自动调用open。每次写完关闭后重新调用该函数,会导致页面被重写
innerHTML则是DOM页面元素的一个属性,代表该元素的html内容,你可以精确到某一个具体的元素来进行更改。如果想修改document的内容,则需要修改document.documentElement.innerElement
innerHTML将内容写入某个DOM节点,不会导致页面全部重绘,innerHTML很多情况下都优于document.write,其原因在于其允许更精准的控制要刷新页面的那个部分

createElement与createDocumentFragment的区别?

共同点:

  1. 添加子元素后返回值都是新添加的子元素
  2. 都可以通过appendChild添加子元素,并且子元素必须是node类型,不能为文本
  3. 若添加的子元素是文档中存在的元素,则通过appendChild在为其添加子元素时,会从文档中删除之存在的元素
    不同点:
  4. createElement创建的是元素节点,节点类型为1,createDocumentFragment创建的是文档碎片,节点类型是11
  5. 通过createElement新建元素必须指定元素tagName,因为其可用innerHTML添加子元素。通过createDocumentFragment则不必
  6. 通过createElement创建的元素是直接插入到文档中,而通过createDocumentFragment创建的元素插入到文档中的是他的子元素
    attribute和property的区别是什么?
    attribute是dom元素在文档中作为html标签拥有的属性;
    property就是dom元素在js中作为对象拥有的属性;
    对于html的标准属性来说,attribute和property是同步的,是会自动更新的,但是对于自定义的属性来说,他们是不同步的

一次性插入1000个div,如何优化插入的性能

使用Fragment(document.createDocumentFragment( ))
Documentfragments是DOM节点,它们不是DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树中。因为文档片段存在于内存中,并不在DOM树中,所以讲子元素插入到文档片段时不会引起页面回流。因为使用文档片段会带来更好的性能
先创建一个div,后续的复制这个元素,避免重复创建元素,再放到元素片段里面

   var divFragment = document.createDocumentFragment(); 
      let div = document.createElement(“div”); 
      forvar i = 0; i <1000; i ++{ 
            divFragment.append(div.cloneNode())
      } 
      document.body.appendChild(divFragment);

JS中几种常见的高阶函数

高阶函数是对其他函数进行操作的函数,可以将它们作为参数或通过返回它们。简单来说,高阶函数是一个函数,它接收函数作为参数或将函数作为输出返回。

typeof和instanceof有什么区别?

typyof用于判断数据的基本类型
instanceof用于判断一个变量是够属于某个对象的实例。也可以用来判断某个构造函数的prototype属性是否存在另外一个要检测对象的原型链上

简述同步和异步的区别?

同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到返回信息之后才继续执行后面的代码
异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。

window.onload和DOMContentLoaded的区别
window.addEventListener(‘load’,function(){
//页面的所有资源加载完才会执行, 包括图片和视频等
})
document.addEventListener(‘DOMContentLoaded’,function(){
//DOM渲染完即可执行, 此时图片,视频还可能没有加载完成
})

document.load和document.redy的区别?

  1. load是当页面所有资源加载完成后(包括DOM文档树,css文件,js文件,图片资源等),执行一个函数
    问题:如果图片资源较多,加载时间较长。onload后等待执行的函数需要等待较长时间,所以一些效果可能受到影响
  2. $(document).ready( )是当DOM文档树加载完成后执行一个函数(不包含图片,css等),所以比load较快执行
    注意:在原生JS中不包括ready( )这个方法,只有load方法就是onload事件

解释一下线程和进程的区别?

进程:是资源分配的基本单词,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配CPU时间,程序开始真正运行。
线程:是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分配的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下九允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。

线程和进程个子有什么区别和优劣势?

  1. 进程是资源分配的最小单位,线程是程序执行的最小单位。
  2. 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
  3. 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点
  4. 但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间

什么是伪数组,如何将伪数组转换为真数组?

伪数组:无法直接调用数组方法或期望length属性有什么特殊的行为,不具有push、pop等方法,但仍可以对真正数组遍历方法来遍历它们。典型的是函数的argument参数等
伪数组的三大特性:

  1. 具有length属性
  2. 按索引方式存储数据
  3. 不具有数组的push,pop等方法
    将伪数组转化为标准数组需要用到数组原型中的方法 slice
    例如: Array.prototype.slice.call( 伪数组名称 );

for in和for of 的区别

  1. for … in循环: 只能获得对象的键名,不能获得键值
    for … of循环: 允许遍历获取键值
  2. 对于普通对象, 没有部署原生的iterator接口, 直接使用for … of 会报错
    可以使用for … in循环遍历键名
    也可以使用Object.keys( obj )方法将对象的键名生成一个数组,然后遍历这个数组
  3. for … in循环不仅遍历数字键名, 还会遍历手动添加的其他键, 甚至包括原型链上的键。for … of则不会这样
  4. forEach循环无法中途跳出,break命令或return命令都不能奏效
    for … of循环可以与break、continue和retun配合使用,跳出循环
  5. 无论是for … in还是for … of都不能遍历出Symbol类型的值, 遍历Symbol类型的值需要用Object.getOwnPropertySymbols( ) 方法
    总之,for … in循环主要是为了遍历对象而生, 不适用于遍历数组
    for … of遍历可以用来遍历数、类数组对象、字符串、Set、Map以及Generator对象

判断是否为数组的方法

console.log( arr instanceof Array )
console.log( arr.construct == Array)
console.log( Array.isArray( arr ))

如何阻止事件冒泡和默认事件?

cancelBunnle(IE)
return false;
event.preventDefault( );
event.stopPropagation( );

JS垃圾回收方式

标记清楚:这是js最常用的垃圾回收方法,当一个变量进入执行环境时,例如函数中声明一个变量,将其标记为进入环境,当变量离开环境时,(函数执行结束),标记为离开环境
引用计数:跟踪记录每个值被引用的次数,声明一个变量,并将引用类型复制给这个变量,则这个值的引用次数+1,当变量的值变成了另一个,则这个值的引用次数-1,当这个值的引用次数为0的时候,就回收

DOM节点的增删改查

  1. 查找
    getElementById( ) getElementsByClassName( ) getElementsByTagName( )
    querySelector( ) 返回第一个匹配的元素 querySelectorAll( ) 返回全部匹配的元素
  2. 插入
    appendChild( ) 末尾插入
    insertBefore( ) 特定位置插入
  3. 替换
    replaceChild( ) 接受两个参数, 第一个为要插入的节点, 第二个为要替换的节点
  4. 删除
    removeChild( )

实现函数A和函数B, 函数B继承函数A

//方式1
function A(){};
function B(){};
B.prototype = new A()
//方式2
function A(){};
function B(){
A.call(this);
};
 
// 方式3
function A(){};
function B(){};
B.prototype = new A();
function B(){
A.call(this)
}

mouseover和mouseenter的区别

mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发冒泡的过程
mouseenter:当鼠标移入元素(除了元素本身, 不包含子元素)会触发的事件

JS的各种位置,比如:clienHeight、scrollHeight、offsetHeight以及scrollTop、offsetTop、clientTop的区别?

clientHeight: 表示的是可视区域的高度, 不包含border和滚动条
offsetHeight: 表示可是区域的高度, 包含了border和滚动条
scrollHeight: 表示了所有区域的高度, 包含了滚动条被隐藏的部分
clientTop: 表示边框border的厚度, 在为指定的情况下,一般为0
scrollTop: 滚动后被隐藏的高度, 获取对象相对于offsetParent属性指定的父坐标距离顶部的距离
展开(Spread)运算符和剩余(Rest)运算符有什么区别?
展开运算符(spread)是三个点(…),可以将一个数组转为用逗号分隔的参数序列。说的通俗易懂一点,有点像化骨绵掌,把一个大元素给打散成一个个单独的小元素
剩余运算符也是用三个点(…)表示, 它的样子看起来和展开运算符一样,但是它是用于结构数组和对象. 在某种程度上, 剩余元素和展开元素相反, 展开元素会”展开”数组变成多个元素, 剩余元素会收集多个元素和”压缩”成一个单一的元素

// 拓展运算符
let arr = ["阿里巴巴",'阿里妈妈','蚂蚁金服'];`在这里插入代码片`
console.log(...arr); //阿里巴巴 阿里妈妈 蚂蚁金服
//剩余运算符
let [webName,...rest] = ["阿里巴巴",'阿里妈妈','蚂蚁金服'];
console.log(webName); //阿里巴巴
console.log(rest); //(2) ["阿里妈妈", "蚂蚁金服"]

你可能感兴趣的:(2020年最新最全的前端面试题整理----原生JS篇)