【面试】web前端经典面试题试题及答案-javaScript

javaScript
原型链、类、继承、作用域、闭包、js运行机制/单线程、js数据类型、js内置函数/内置对象、js去重、js逻辑判断、js内存泄漏、dom、bom、iframe、通信、ajax、错误监控、js基础


  • 原型链

Function.proto(getPrototypeOf)是什么??

参考答案
获取一个对象的原型,在chrome中可以通过__proto__的形式,或者在ES6中可以通过Object.getPrototypeOf的形式。
那么Function.proto是什么么?也就是说Function由什么对象继承而来,我们来做如下判别。
Function.proto==Object.prototype //false
Function.proto==Function.prototype//true
我们发现Function的原型也是Function。
我们用图可以来明确这个关系:
图片
在这里插入图片描述

简述一下原型 / 构造函数 / 实例?

参考答案
原型(prototype): 一个简单的对象,用于实现对象的 属性继承。可以简单的理解成对象的爹。在 Firefox 和 Chrome 中,每个JavaScript对象中都包含一个__proto__(非标准)的属性指向它爹(该对象的原型),可obj.__proto__进行访问。
构造函数: 可以通过new来 新建一个对象的函数。
实例: 通过构造函数和new创建出来的对象,便是实例。实例通过__proto__指向原型,通过constructor指向构造函数。
这里来举个栗子,以Object为例,我们常用的Object便是一个构造函数,因此我们可以通过它构建实例。

// 实例
const instance = new Object()

则此时, 实例为instance, 构造函数为Object,我们知道,构造函数拥有一个prototype的属性指向原型,

因此原型为:

// 原型
const prototype = Object.prototype

这里我们可以来看出三者的关系:

实例.__proto__ === 原型

原型.constructor === 构造函数

构造函数.prototype === 原型

// 这条线其实是是基于原型进行获取的,可以理解成一条基于原型的映射线
// 例如: 
// const o = new Object()
// o.constructor === Object   --> true
// o.__proto__ = null;
// o.constructor === Object   --> false
实例.constructor === 构造函数

创建对象有几种方法?

1、字面量对象 // 默认这个对象的原型链指向object
var o1 = {name: '01'};
2、通过new Object声明一个对象
var o11 = new Object({name: '011'});
3、使用显式的构造函数创建对象

var M = function(){this.name='o2'};
var o2 = new M();
o2.__proto__=== M.prototype

o2的构造函数是M
o2这个普通函数,是M这个构造函数的实例
4、object.create()

var P = {name:'o3'};
var o3 = Object.create(P);
  • 原型、构造函数、实例、原型链

在这里插入图片描述
1、Object.prototype属性是整个原型链的顶端
2、原型链通过prototype原型和__proto__属性来查找的.
3、所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了“null”以外)。
4、所有的引用类型(数组、对象、函数),都有一个__proto__属性,属性值是一个普通的对象(null除外)。
5、所有的函数,都有prototype属性,属性值也是一个普通的对象。
6、所有的引用类型(数组、对象、函数),__proto__属性指向它的构造函数prototype属性值
7、实例本身的属性和方法如果没有找到,就会去找原型对象的属性和方法。如果在某一级找到了,就会停止查找,并返回结果

列举出集中创建实例的方法?

参考答案
1.字面量

let obj={'name':'张三'}
2.Object构造函数创建
let Obj=new Object()
Obj.name='张三'

3.使用工厂模式创建对象

function createPerson(name){
 var o = new Object();
 o.name = name;
 };
 return o; 
}
var person1 = createPerson('张三');

4.使用构造函数创建对象

function Person(name){
 this.name = name;
}
var person1 = new Person('张三');

instanceof的原理?

实例对象的__proto__属性和构造函数的prototype属性,判断是不是同一个引用
在这里插入图片描述1、实例对象的属性引用的是构造函数的原型对象
2、instanceof用于判断引用类型属于哪个构造函数的方法

var M = function(name) {this.name = name};
var o3 = new M('o3');
console.log(o3 instanceof M); // true
console.log(o3 instanceof Object); // true,只要是原型链上的构造函数,都会被看成是object的构造函数,都会返回true
console.log(o3.__proto__===M.prototype); // true
console.log(M.prototype.__proto__===Object.prototype); // true
console.log(o3.__proto__.constructor === M); // true,o3是M这个构造函数直接生成的
console.log(o3.__proto__.constructor === Object); // false
  • new运算符

new运算符后面跟的是一个构造函数
在这里插入图片描述

var new2 = function(func) {
	var o = Object.create(func.prototype);
	var k = func.call(o); // call转移上下文
	if (type k === 'Object') {
		return k;
	} else {
		return o;
	}
}
var o6 = new2(M);
console.log(o6 instanceof M); // true
o6 instanceof Object // true
o6.__proto__.constructor === M; // true
M.prototype.walk = function(){console.log('walk')};
o6.walk(); // 能成功

类的声明?

1、传统的构造函数,声明一个类

  function Animal() {
    this.name = 'name';
  }

2、es6中的class声明

  class Animal2{
    constructor() {
      this.name = name;
    }
  }

生成实例?/ 声明一个类,怎么生成类的实例?

 /*实例化*/
console.log(new Animal(), new Animal2()); // 通过New就可以实例化一个类,如果没有参数,Animal后面的()可以不要

  • 继承

继承的本质是原型链

简述一下JS继承,并举例?

参考答案
在 JS 中,继承通常指的便是 原型链继承,也就是通过指定原型,并可以通过原型链继承原型上的属性或者方法。
最优化: 圣杯模式

var inherit = (function(c,p){
  var F = function(){};
  return function(c,p){
      F.prototype = p.prototype;
      c.prototype = new F();
      c.uber = p.prototype;
      c.prototype.constructor = c;
  }
})();

使用 ES6 的语法糖 class / extends

call、apply的共同点与区别?

1、改变了函数运行上下文
2、call()和apply()主要是能扩充函数赖以运行作用域。两者的作用方式相同,它们的区别在于接收参数的方式不同,对于call()而言,第一个参数this与apply()相同,其他的参数必须直接传给函数,要一个一个的列出来,而对于apply()来说,apply()可以接收一个数组或arguments对象。所以如何选择二者,在于哪种给函数传参数的方式最简单。

说说bind、call、apply 区别??

call 和 apply 都是为了解决改变 this 的指向。作用都是相同的,只是传参的方式不同。
除了第一个参数外,call 可以接收一个参数列表,apply 只接受一个参数数组。

let a = {
    value: 1
}
function getValue(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value)
}
getValue.call(a, 'yck', '24')
getValue.apply(a, ['yck', '24'])

bind和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过 bind实现柯里化。

(下面是对这三个方法的扩展介绍)
如何实现一个 bind 函数
对于实现以下几个函数,可以从几个方面思考
不传入第一个参数,那么默认为 window
改变了 this 指向,让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除?

Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  var _this = this
  var args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

如何实现一个call函数

Function.prototype.myCall = function (context) {
  var context = context || window
  // 给 context 添加一个属性
  // getValue.call(a, 'yck', '24') => a.fn = getValue
  context.fn = this
  // 将 context 后面的参数取出来
  var args = [...arguments].slice(1)
  // getValue.call(a, 'yck', '24') => a.fn('yck', '24')
  var result = context.fn(...args)
  // 删除 fn
  delete context.fn
  return result
}

如何实现一个apply函数

Function.prototype.myApply = function (context) {
  var context = context || window
  context.fn = this

  var result
  // 需要判断是否存储第二个参数
  // 如果存在,就将第二个参数展开
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }

  delete context.fn
  return result
}

用javascript实现对象的继承/ 继承的几种方式,这几种方式的优缺点?

方法1:借助构造函数实现继承(部分继承)

  /**
   * 借助构造函数实现继承
   */
  function Parent1() {
    this.name = 'parent';
  }
  Parent1.prototype.say = function() {}; // 不会被继承
  function Child1() {
    // 继承:子类的构造函数里执行父级构造函数
    // 也可以用apply
    // parent的属性都会挂载到child实例上去
    // 借助构造函数实现继承的缺点:①如果parent1除了构造函数里的内容,还有自己原型链上的东西,自己原型链上的东西不会被child1继承
    // 任何一个函数都有prototype属性,但当它是构造函数的时候,才能起到作用(构造函数是有自己的原型链的)
    Parent1.call(this);
    this.type = 'child1';
  }
  console.log(new Child1);

(1)如果父类的属性都在构造函数内,就会被子类继承。
(2)如果父类的原型对象上有方法,子类不会被继承。
方法2:借助原型链实现继承

/**
   * 借助原型链实现继承
   */
  function Parent2() {
    this.name = 'name';
    this.play = [1, 2, 3]
  }
  function Child2() {
    this.type = 'child2';
  }
  Child2.prototype = new Parent2(); // prototype使这个构造函数的实例能访问到原型对象上
  console.log(new Child2().__proto__);
  console.log(new Child2().__proto__ === Child2.prototype); // true

  var s1 = new Child2(); // 实例
  var s2 = new Child2();
  console.log(s1.play, s2.play);
  s1.play.push(4);

  console.log(s1.__proto__ === s2.__proto__); // true // 父类的原型对象

(1)原型链的基本原理:构造函数的实例能访问到它的原型对象上
(2)缺点:原型链中的原型对象,是共用的
方法3:组合方式

 /**
   * 组合方式
   */
  function Parent3() {
    this.name = 'name';
    this.play = [1, 2, 3];
  }
  function Child3() {
   Parent3.call(this);
   this.type = 'child3';
  }
  Child3.prototype = new Parent3();
  var s3 = new Child3();
  var s4 = new Child3();
  s3.play.push(4);
  console.log(s3.play, s4.play);
  // 父类的构造函数执行了2次
  // 构造函数体会自动执行,子类继承父类的构造函数体的属性和方法

组合方式优化1:

  /**
   * 组合继承的优化方式1:父类只执行了一次
   */
  function Parent4() {
    this.name = 'name';
    this.play = [1, 2, 3];
  }
  function Child4() {
    Parent4.call(this);
    this.type = 'child4';
  }
  Child4.prototype = Parent4.prototype; // 继承父类的原型对象
  var s5 = new Child4();
  var s6 = new Child4();
  console.log(s5 instanceof Child4, s5 instanceof Parent4); // true
  console.log(s5.constructor); // Parent4  //prototype里有个constructor属性,子类和父类的原型对象就是同一个对象, s5的constructor就是父类的constructor

组合方式优化2(最优解决方案):

   /**
   * 组合继承优化2
   */
  function Parent5() {
    this.name = 'name';
    this.play = [1, 2, 3];
  }
  function Child5() {
    Parent5.call(this);
    this.type = 'child5';
  }
  Child5.prototype = Object.create(Parent5.prototype);  // Object.create创建的对象就是参数
   Child5.prototype.constructor = Child5;
   var s7 = new Child5();
   console.log(s7 instanceof Child5, s7 instanceof Parent5);
   console.log(s7.constructor); // 构造函数指向Child5

二、优缺点:
原型链继承的缺点
1、字面量重写原型
一是字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。
2、借用构造函数(类式继承)
借用构造函数虽然解决了刚才两种问题,但没有原型,则复用无从谈起。所以我们需要原型链+借用构造函数的模式,这种模式称为组合继承
3、组合式继承
组合式继承是比较常用的一种继承方法,其背后的思路是 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性。

延伸:
一个对象,是继承了某个类,问你他的原型链?

ES5的继承和ES6的继承有什么区别?

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


  • 作用域

1、js没有块级作用域,有函数作用域、全局作用域。
es6出现才有块级作用域。

说说你对作用域链的理解?

作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。

this?

1、作为构造函数执行

function Foo(name) {
	this.name = name;
}
var f = new Foo('zhangsan');

2、作为对象属性执行

var obj = {
	name: 'A',
	printName: function() {
		console.log(this.name);
	}
}
obj.printName();

3、作为普通函数执行

function fn() {
	console.log(this);
}
fn();

4、call apply bind

function fn1(name, age) {
	alert(name);
	console.log(this);
}
fn1.call({x: 100}, 'zhangsan', 20);
fn1.apply({x:100}, ['zhangsan', 20])
var fn2 = function (name, age) { // 必须是函数表达式,不能是函数声明,即不能是function fn2(name, age) {}
	alert(name);
	console.log(this);
}.bind({y:200});
fn2('zhangsan', 20);

请说出下列的值?

 var items = document.getElementsByTagName('li');
  var i,x;
  i = 0;
  l = items.length;
  for (; i < x; i++) {
    items[i].addEventListener('click', function() {
      console.log(i);
    })
  }

x
1、i是全局变量,从0增加到x-1,最后一次x++后成为c成为x。最终点击输出的就是x。

请说出下列的值?

 !function() {
 	'use strict';
    str = 'A Ha~';
    console.log(window.str);
    var str = 'Haha~';
    console.log(str);
  }();

undefined Haha~
1、console.log(window.str)改成cnosole.log(this.str), 说出this.str打印出来的值
非严格模式下:undefined
严格模式下:报错。因为严格模式下,this为undefined,所以this.str报错


  • javaScripti闭包

1、闭包是函数和声明该函数的词法环境的组合。
2、使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在js中,函数即闭包,只有函数才会产生作用域的概念

闭包的特征?

1、函数嵌套函数
2.、函数内部可以引用外部的参数和变量
3、参数和变量不会被垃圾回收机制回收

闭包应用场景?

1、作为返回值

function fn() {
	var max = 10;
	return function bar(x) {
		if (x > max) {
			console.log(x);
		}
	}
}
var f1 = fn();
f1(15);

2、作为参数传递

var max = 10;
function fn(x) {
	if (x > max) {
		console.log(x);
	}
}
(function(f) {
	var max = 100;
	f(15);
})(fn);

实际开发中闭包的应用?

闭包实际应用中主要用于封装变量,收敛权限

function isFirstLoad() {
	var _list = []; // 有_的变量说明是私有变量,函数内部使用的
	return function(id) {
		if (_list.indexOf(id) >=0) { // 也可用includes
			return false;
		} else {
			 _list.push(id);
			 return true;
		}
	}
}
// 使用
var firstLoad = isFirstLoad();
console.log(firstLoad(10)); // true
console.log(firstLoad(10)); // false
console.log(firstLoad(20)); // true
// 你在isFirstLoad函数外面,根本不可能修改掉_list的值

请说出下列的值?

function num(a) {
  'use strict';
  var n = 10;
  return function (b) {
    n +=10;
    return (a + b + n);
  }
}
var n = num(10);
console.log(n(10)); 
console.log(n(10));

40 50
1、 40 = 10 + 10 + 20;
2、50 = 10 + 10 + 30;

请说出下列的值?

function obj() {
  this.name = 'Hu';
}

obj.prototype.getName = function() {
  return this.name;
}

obj.prototype.delayCall = function() {
  window.setTimeout(function() {
    console.log(this.getName());
  }, 300);
}

var o = new obj();
o.delayCall();

报错:this.getName() is not a function
1、这个时候的this指向window

请说出下列的值?

function fun(n, o) {
  console.log(o);
  return {
    fun: function(m) {
      return fun(m, n);
    }
  }
}
var a = fun(0);
a.fun(1);
a.fun(2);
a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1);
c.fun(2);
c.fun(3);

undefined 0 0 0 0
undefined 0 1 2
undefined 0
1
1


  • js运行机制/ 单线程/ 异步

(2)javascript是单线程的,主线程拥有一个执行栈以及一个任务队列,主线程会依次执行代码,当遇到异步函数时候,会先将该函数入栈,所有主线程函数运行完毕后再将异步函数出栈,直到所有的异步函数执行完毕即可。
(3)Macrotask(宏任务)和Microtask(微任务)
都属于上述的异步任务中的一种,他们分别有如下API:
macrotask: setTimeout, setInterval, setImmediate, I/O, UI rendering
microtasks: process.nextTick, Promise, MutationObserver
(4)promise中的then方法的函数会被推入到microtasks队列中,而setTimeout函数会被推入到macrotasks
任务队列中,在每一次事件循环中,macrotask只会提取一个执行,而microtask会一直提取,直到microsoft队列为空为止。

如何理解js的单线程?

只有一个线程,同一时间只能做一件事情。

js为什么是单线程的?

避免dom渲染的冲突
1、浏览器需要渲染dom
2、js可以修改dom结构
3、js执行的时候,浏览器dom渲染会暂停
4、两段js也不能同时执行(都修改dom就冲突了)
5、webworder支持多线程,但是不能访问dom

同步和异步的区别是什么?分别举一个同步和异步的例子?

1、同步会阻塞代码执行,而异步不会。
2、alert是同步,setTimeout是异步。

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

何时需要异步?

1、在可能发生等待的情况,等待也是占线程的一种
2、等待过程中不能像alert一样阻塞程序进行
3、因此,“等待的情况”都需要异步

什么是任务队列?

任务队列(task queue)主要分两种:
1、宏任务(macrotask):在新标准中叫task
(1)主要包括:script(整体代码),setTimeout,setInterval,setImmediate,I/O,ui rendering
2、微任务(microtask):在新标准中叫jobs
(1)主要包括:process.nextTick, Promise,MutationObserver(html5新特性)

扩展:
1、同步任务:在主线程上,排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
2、异步任务:不进入主线程,而进入“任务队列”(task queue)的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

请说出下列值?

setTimeout(() => {
  console.log('1')
  new Promise((resolve) => {
    resolve()
  }).then(() => {
    console.log('2')
  })
}, 0);

setTimeout(() => {
  console.log('3')
}, 0);

new Promise((resolve) => {
  resolve()
}).then(() => {
  console.log('4')

  new Promise((resolve) => {
    resolve()
  }).then(() => {
    console.log('5')
  })

  setTimeout(() => {
    console.log('6')
  }, 0);
})

new Promise((resolve) => {
  resolve()
}).then(() => {
  console.log('7')
})

4,7,5,1,2,3,6

setTimeout、Promise、Async/Await 的区别

参考:https://gongchenghuigch.github.io/2019/09/14/awat/

定时器的执行顺序或机制?

因为js是单线程的,浏览器遇到setTimeout或者setInterval会先执行完当前的代码块,在此之前会把定时器推入浏览器的待执行事件队列里面,等到浏览器执行完当前代码之后会看一下事件队列里面有没有任务,有的话才执行定时器的代码。所以即使把定时器的时间设置为0还是会先执行当前的一些代码。

function test(){
    var aa = 0;
    var testSet = setInterval(function(){
        aa++;
        console.log(123);
        if(aa<10){
            clearInterval(testSet);
        }
    },20);
  var testSet1 = setTimeout(function(){
    console.log(321)
  },1000);
  for(var i=0;i<10;i++){
    console.log('test');
  }
}
test()

输出结果:

test //10次
undefined
123
321

栈和队列的区别?

1、栈的插入和删除操作都是在一端进行的,而队列的操作却是在两端进行的。
2、队列先进先出,栈先进后出。
3、栈只允许在表尾一端进行插入和删除,而队列只允许在表尾一端进行插入,在表头一端进行删除

栈和堆的区别?

1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。
堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。
2、堆(数据结构):堆可以被看成是一棵树,如:堆排序;
栈(数据结构):一种先进后出的数据结构。

  • event loop

什么是event loop?

事件循环。
在这里插入图片描述
1、js实现异步的具体解决方案:event-loop
2、运行栈:执行同步任务的
3、浏览器js引擎遇到了setTimeout,识别了这是一个异步任务,不会将其放入运行栈,而是把它拿走,拿走了之后也没有立马放到异步任务队列中,按延迟时间放入到任务队列中。同步任务没有正在执行的东西,就会读异步任务,把任务放到运行栈中,执行完了又去读异步任务,把任务放到运行栈中,如此循环。

event-loop流程?

1、同步代码,直接执行
2、异步先放在任务队列中
3、待同步函数执行完毕,轮询执行任务队列的函数

  • 任务队列

哪些语句会放入异步任务队列中?

1、定时任务:setTimeout、setInterval
2、网络请求:ajax请求、动态<img加载

console.log('start');
var img = document.createElement('img');
img.onload = function() {
	console.log('loaded');
}
img.src = 'https://ss0.baidu.com/60NW/a.jpg';
console.log('end');
// 打印出来的是start, end, loaded

3、事件绑定:dom事件
4、ES6中的promise.then中的函数
Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的。

何时被放入任务队列?

1、类似onclick等,由浏览器内核的DOM binding模块处理,事件触发时,回调函数添加到任务队列中;
2、setTimeout等,由浏览器内核的Timer模块处理,时间到达时,回调函数添加到任务队列中;
3、Ajax,由浏览器内核的Network模块处理,网络请求返回后,添加到任务队列中。

ajax加载完成,即ajax什么时候success,就什么时候把ajax中的函数放入到异步队列中


  • js数据类型

1、值类型/ 基本数据类型:undefined、string、number、boolean
引用类型:对象、数组、函数
2、复杂数据类型Object包括3种引用类型。
3、基本数据类型存储在栈中,复杂数据类型存储在堆中。
4、值类型:不会因为赋值而相互干扰。
5、false: 0、NaN、’’、null、undefined

var obj = {};
if (obj.a == null) {} // 判断a这个属性是否存在
function(a, b){if (a == null) {}} // 判断a这个参数是否存在

js使用typeof能得到的哪些类型?

typeof只能区分值类型

typeof undefined // undefined
typeof null // object
typeof console.log // function
typeof NaN // number

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

instanceof Array

js变量按照存储方式区分为哪些类型,并描述其特点?

1、存储在栈中:值类型。
2、存储在堆中:引用类型

引用类型的”数据“存储在堆中,
引用类型”指向堆中的数据的指针“存储在栈中。

null和undefined的区别?

1、null是一个表示”无”的对象,是只有一个值的特殊类型,转为数值时为0;
undefined是一个表示”无”的原始值,表示一个空对象引用,转为数值时为NaN。
2、当声明的变量还未被初始化时,变量的默认值为undefined。
3、null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。
undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义。
4、null 和 undefined 的值相等,但类型不等

undefined的典型用法?

1、变量被声明了,但没有赋值时,就等于undefined。
2、调用函数时,应该提供的参数没有提供,该参数等于undefined。
3、对象没有赋值的属性,该属性的值为undefined。
4、函数没有返回值时,默认返回undefined。

null的典型用法?

1、作为函数的参数,表示该函数的参数不是对象。
2、作为对象原型链的终点。

chrome60+浏览器中,a===b的是哪项?

B

// A
var a = b = 1;
b = 2;

// B
var a = {name: 'jack', age: 27};
var b = a;
b.name = 'may';

// C
var a = [1, 3, 5];
var b = [...a];

// D
var a = [1, 2, 3];
var b = a.push(4); // b = 4;

1、===,绝对等于(值和类型均相等)。
2、C选项:数组是复合数据结构,b是对a的复制,修改b不会对a产生影响。a、b的类型相同,但是值不同。

如何判断 0.1 + 0.2 与 0.3 相等?

作为一道面试题,我觉得重要的是要讲出一点其他人一般不会答出来的深度。像这道题,可以从原理和解决方案两个地方作为答题点,最好在编一个案例。大致讲自己遇到过这个问题,于是很好奇深入研究了一下,发现是浮点数精度导致……原理怎样怎样……然后又看了业界的库的源码,然后怎样怎样解决。
关于原理,参照文章https://github.com/mqyqingfeng/Blog/issues/155来解释,实际回答的时候,我觉得答出来
非是 ECMAScript 独有
IEEE754 标准中 64 位的储存格式,比如 11 位存偏移值
其中涉及的三次精度丢失
就已经 OK 了。
再讲解决方案,这个可以直接搜索到,各种方案都了解一下,比较一下优劣,还可以参考业界的一些库的实现,比如 math.js,不过相关的我并没有看过……
如果还有精力的话,可以从加法再拓展讲讲超出安全值的数字的计算问题。
所以我觉得一能回答出底层实现,二能回答出多种解决方案的优劣,三能拓展讲讲 bignum 的处理,就是一个非常不错的回答了。


  • js中的内置函数/内置对象

js中有哪些内置函数/ 数据封装类对象?

内置函数:
Number、String、Boolean
Array、Object、Function
Date、RegExp、Error

js中有哪些内置对象?

内置对象:
Math,Json

js变量按照存储方式区分为哪些类型,并描述其特点?

1、值类型和引用类型。
2、值类型存储的是值 ,赋值之后原变量的值不改变 。
3、 引用类型存储的是地址 ,赋值之后是把原变量的引用地址赋值给新变量 ,新变量改变原来的会跟着改变。

字符串方法/ String对象方法?

String对象方法:
concat() includes indexOf() lastIndexOf() slice() toString() valueOf()

charAt() charCodeAt() endsWith fromCahrCode() match() repeat() replace() search() split startsWith() substr() substring() toLowerCase() toUpperCase() trim() toLocaleLowerCase toLocaleUpperCase()

http://www.runoob.com/jsref/jsref-obj-string.html
字符串属性:
constructor length prototype

数组方法/ Array对象方法?

Array对象方法:
concat() includes indexOf() lastIndexOf() slice() toString() valueOf()

copyWithin() entries() every() fill() filter() find() findIndex() forEach() from() isArray() join() keys() map() pop() push() reduce() reduceRaight() reverse() shift() some() sort() splice() ushift()

http://www.runoob.com/jsref/jsref-obj-array.html

数组方法参数返回值
splice()index(必需)、howmany(必需)、item1…item3(可选):从何处添加/删除元素、应该删除多少元素、要添加到数组的新元素返回被删除元素的数组

数组属性:
constructor length prototype

数组API?

1、forEach 遍历所有元素

var arr = [1, 2, 3];
arr.forEach(function(item, index) {
	// 	遍历数组的所有元素
	console.log(index, item);
});

2、every 判断所有元素是否都符合条件

var arr = [1, 2, 3];
var arr1 = arr.every(function(item, index) {
	if (item < 4) {
		return true;
	}
})
console.log(arr1); // true

3、some 判断是否有至少一项元素符合条件

var arr = [1, 2, 3];
var result = arr.some(function(item, index) {
	if (item < 2) {
		return true;
	}
})
console.log(result); // true

4、sort 排序

var arr = [1, 5, 2, 7, 3, 4];
var arr2 = arr.sort(function(a, b) {
	// 从小到大
	return a-b;
	// 从大到小
	return b-a;
})
console.log(arr2); // 1,2,3,4,5,7

5、map 对元素重新组装,生成新数组

var arr = [1, 5, 2, 7, 3, 4];
var arr2 = arr.map(function(item, index) {
	return '<b>' + item + '</br>';
})
console.log(arr2);

6、filter 过滤符合条件的元素

var arr = [1, 2, 3, 4];
var arr2 = arr.filter(function(item, index) {
	if (item>2) {
		return true;
	}
})
console.log(arr2); // [3, 4]

对象API?

1、for in

var obj = {x:100, y:200, z:300};
var key;
for(key in obj) {
	if (obj.hasOwnProperty(key)) {
		console.log(key);
	}
}


  • 数组去重

数组怎么去重?(方法)

1、使用数组方法indexOf来判断

function sele(arr){
    var temp = [];
    for( var i = 0 ; i < arr.length ; i++ ){
        if( temp.indexOf( arr[ i ] ) == -1 ){
            temp.push( arr[ i ] );
        }
    }
    return temp;
}
var arr = ['aa', 'bb', 'cc', '', 1, 0, '1', 1, 'bb', null, undefined, null];
console.log(sele(arr));

2个缺点,一是效率问题,因为加上indexOf相当于是2重循环,二是indexOf的兼容性问题:IE8–不兼容。
2、使用数组方法indexOf第二种方法 IE8–不兼容

function sele( arr ) {
    var temp = [];
    for( var i = 0 ; i < arr.length ; i++ ){
        if( arr.indexOf( arr[ i ] ) == i ){
            temp.push( arr[ i ] );
        }
    }
    return temp;
}

比方法(1)效率还要差
3、循环

function sele( arr ) {
    var temp = [];
    for( var i = 0 ; i < arr.length ; i++ ){
        for( var j = i + 1 ; j < arr.length ; j++ ){
            if( arr[ i ] === arr[ j ] ){
                j = ++i;
            }
        }
        temp.push( arr[ i ] );
    }
    return temp;
}

4、

function unique3(array)
{
	var result = [];
	var hash = {};
	for(var i=0; i<array.length; i++)
	{
		var key = (typeof array[i]) + array[i];
		if(!hash[key])
		{
			result.push(array[i]);
			hash[key] = true;
		}
	}
	return result;
}
var arr = ['aa', 'bb', 'cc', '', 1, 0, '1', 1, 'bb', null, undefined, null];
console.log(unique3(arr));

以上方法中之所以给key添加了类型前缀,是因为要区分’1’和1。

5、使用es6中includes方法

function sele( arr ) {
    var temp = [];
    arr.forEach( ( v ) => {
        temp.includes( v ) || temp.push( v );
    } )
    return temp;
}

6、es6的set

function unique(array){return Array.from(new Set(array));}
// 或者写成(建议写法)
const unique = arr => [...new Set(arr)]
// 也可以是
const unique = arr => {return [...new Set(arr)]}

var arr = ['aa', , '', 1, 0, '1', 1, , null, undefined, null];
console.log(unique(arr));

对上述数组去重方法速度比较?(性能)

console.time, console.timeEnd

var testArray = [];
for(var i=0; i<500000; i++)
{
	testArray.push(parseInt(Math.random()*100));
}
function test(fn, name)
{
	console.time(name);
	fn(testArray);
	console.timeEnd(name);
}
test(unique1, '第1种实现');
test(unique2, '第2种实现');
test(unique3, '第3种实现');
test(unique4, '第4种实现');

一句话数组去重?

new Set():Set本身是一个构造函数,用来生成Set数据结构

const unique = arr => [...new Set(arr)]
var arr = ['aa', , '', 1, 0, '1', 1, , null, undefined, null];
console.log(unique(arr));

保留数组中非重复元素?

indexOf 是查某个指定的字符串在字符串首次出现的位置(索引值从左往右0、1、2…) (也就是从前往后查)
lastIndexOf 是从右向左查某个指定的字符串在字符串中最后一次出现的位置(索引值从左往右0、1、2…)(也就是从后往前查)

let arr = [11, 23, 26, 23, 11, 9]
const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i))
console.log(filterNonUnique(arr));  // [ 26, 9 ]

保留数组中重复元素?

let arr = [11, 23, 26, 23, 11, 9]
const filterUnique = arr => arr.filter(i => arr.indexOf(i) !== arr.lastIndexOf(i))
console.log(filterUnique(arr)); // [ 11, 23, 23, 11 ]

  • js逻辑判断

一、 || 判断
1、只要‘||’前面为false,无论’||‘后面是true还是false, 结果都返回’||‘后面的值。
2、只要’||‘前面是true,无论’||‘后面是true还是false,结果都返回’||‘前面的值。
二、&& 判断
1、只要‘&&’前面是false,无论’&&‘后面是true还是false,结果都返回’&&‘前面的值。
2、只要’&&‘前面是true,无论’&&‘后面是true还是false,结果都返回’&&‘后面的值。
三、优先级顺序中,逻辑’&&‘的优先级高于逻辑’||’。

请写出下面的答案?

var x, y, z;
x = 0;
y = 1;
z = 2;
x = x || y;
z = y && z;
console.log(x, z);

1, 2


  • 内存泄漏

内存泄漏指任何对象在您不再拥有或需要它之后仍然存在

哪些操作会造成内存泄漏?

1、垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的惟一引用是循环的,那么该对象的内存即可回收。
2、setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
3、闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)

js内存泄漏的解决方式

先看看这个:以后再慢慢整理:http://www.cnblogs.com/carekee/articles/1733847.html

1、global variables:对未声明的变量的引用在全局对象内创建一个新变量。在浏览器中,全局对象就是 window。

function foo(arg) {
	bar = 'some text'; // 等同于window.bar = 'some text';
}

(1)解决:
①创建意外的全局变量

function foo() {
	this.var1 = 'potential accident'
}

②可以在 JavaScript 文件开头添加 “use strict”,使用严格模式。这样在严格模式下解析 JavaScript 可以防止意外的全局变量。
③在使用完之后,对其赋值为 null 或者重新分配。

2、被忘记的 Timers 或者 callbacks
(1)解决:
3、闭包:一个可以访问外部(封闭)函数变量的内部函数。
(1)解决:
4、DOM 引用
(1)解决:


  • dom

1、dom:浏览器把拿到的html代码,结构化一个浏览器能识别并且js可操作的一个模型

dom是哪种基本的数据结构?

dom操作的常用api有哪些?

1、获取dom节点

document.getElementById('div1');
document.getElementsByTagName('div');
document.getElementsByClassName('container');
document.querySelector('p');
document.querySelectorAll('p');

2、property(js对象的property)

var p = document.getElementByTagName('p')[0];
console.log(p.nodeName); // nodeName是p的property,即nodeName是p的属性

3、attribute

p.getAttribute('data-name');
p.setAttribute('data-name', 'imooc');

dom节点的attribute和property有何区别?

1、property:一个js对象的属性的修改
2、attribute:对html标签属性的修改

dom结构操作/ 怎样添加、移除、移动、复制、创建和查找节点/ dom操作的常用api?

1、创建新节点

createDocumentFragment()    //创建一个DOM片段
createElement()   //创建一个具体的元素
createTextNode()   //创建一个文本节点

2、添加、移除、替换、插入

appendChild()
removeChild()
replaceChild()
insertBefore() //并没有insertAfter()

3、查找

getElementsByTagName()    //通过标签名称
getElementsByName()    //通过元素的Name属性的值(IE容错能力较强,
会得到一个数组,其中包括id等于name值的)
getElementById()    //通过元素Id,唯一性
  • dom事件

dom事件的级别?

1、dom0 element.οnclick=function(){}
2、dom2 element.addEventListener(‘click’, function(){}, false) // 默认是false。false:冒泡阶段执行,true:捕获阶段产生。
3、dom3 element.addEventListener(‘keyup’, function(){}, false) // 事件类型增加了很多,鼠标事件、键盘事件

dom事件模型?

捕获:从上到下
冒泡:从当前元素往上

dom事件流?

浏览器在为当前页面与用户交互的过程中,比如说点击鼠标左键,左键怎么传到页面上,这就是事件流,他又是怎么响应的。
捕获-> 目标阶段->冒泡

描述dom事件捕获的具体流程?

window-> document-> html-> body-> … -> 目标元素
document.documentElement();获取到html
document.body获取到body

event对象的常见应用?

1、event.preventDefault(); // 阻止默认行为,阻止a链接默认的跳转行为
2、event.stopPropagation(); // 阻止冒泡
3、event.stopImmediatePropagation(); // 按钮绑定了2个响应函数,依次注册a,b两个事件,点击按钮,a事件中加event.stopImmediatePropagation()就能阻止b事件
4、event.currentTarget // 早期的ie不支持,当前绑定的事件
event.target

自定义事件/ 模拟事件?

1、给一个按钮自己增加一个事件,在其他地方触发,而不是用回调的方式触发

var ev = document.getElementById('ev');
var eve = new Event('custome'); // eve:事件对象
ev.addEventListener('custome', function(){
	console.log('custome');
});
ev.dispatchEvent(eve);

2、customeEvent

通用事件绑定/ 编写一个通用的事件监听函数?

function bindEvent(elem, type, selector, fn) {
	if (fn == null) {
		fn = selector;
		selector = null;
	}
	elem.addEventListner(type, function(e) {
		var target;
		if (selector) {
			target = e.target;
			if (target.matches(selector)) {
				fn.call(target, e);
			}
		} else {
			fn(e);
		}
	})
}
// 使用代理
var div1 = document.getElementById('div1');
bindEvent(div1, 'click', 'a', function(e) {
	console.log(this.innerHTML);
});
// 不使用代理
var a = document.getElementById('a1');
bindEvent(div1, 'click', function(e) {
	console.log(a.innerHTML);
})

1、代理的好处
(1)代码简洁
(2)减少浏览器内存占用
2、事件冒泡
事件冒泡的应用:代理

dom树、render树的关系/ dom树、render树的区别(第2、3条)?

1、dom树,css树合并成成渲染树(render树)
2、DOM树与HTML标签一一对应,包括head和隐藏元素
3、渲染树不包括head和隐藏元素,大段文本的每一个行都是独立节点,每一个节点都有对应的css属性


  • bom

1、bom:bower object model

bom常用属性?

1、navigator

var ua = navigator.userAgent;
ua.indexOf('chrome');

2、screen

screen.width
screen.height

3、location

location.href
location.protocol // http: https
location.host // learn/191
location.pathname
location.search
location.hash

4、history

history.back();
history.forward();

什么是Bom?有哪些常用的Bom属性??

Bom是浏览器对象

  • location对象
    location.href-- 返回或设置当前文档的URL
    location.search – 返回URL中的查询字符串部分。例如 http://www.dreamdu.com/dreamd… 返回包括(?)后面的内容?id=5&name=dreamdu
    location.hash – 返回URL#后面的内容,如果没有#,返回空 location.host – 返回URL中的域名部分,例如www.dreamdu.com
    location.hostname – 返回URL中的主域名部分,例如dreamdu.com
    location.pathname – 返回URL的域名后的部分。例如 http://www.dreamdu.com/xhtml/ 返回/xhtml/
    location.port – 返回URL中的端口部分。例如 http://www.dreamdu.com:8080/xhtml/ 返回8080
    location.protocol – 返回URL中的协议部分。例如 http://www.dreamdu.com:8080/xhtml/ 返回(//)前面的内容http:
    location.assign – 设置当前文档的URL
    location.replace() – 设置当前文档的URL,并且在history对象的地址列表中移除这个URL location.replace(url);
    location.reload() – 重载当前页面
  • history对象
    history.go() – 前进或后退指定的页面数
    history.go(num); history.back() – 后退一页
    history.forward() – 前进一页
  • Navigator对象
    navigator.userAgent – 返回用户代理头的字符串表示(就是包括浏览器版本信息等的字符串)
    navigator.cookieEnabled – 返回浏览器是否支持(启用)cookie

  • iframe

iframe父子组件之间的通信方式?

1、


iframe的优缺点?

1、

  • 通信

什么是同源策略及限制?

1、同源策略:
同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。

源:协议、域名、端口
2、限制:
(1)Cookie、localStorage 和indexDB无法读取。
(2)DOM无法获得。
(3)ajax请求不能发送

前后端如何通信?

1、ajax(同源)
2、websocket
3、cors

  • 跨域

1、跨域注意事项
(1)所有的跨域请求都必须经过信息提供方允许。
(2)如果未经允许即可获取,那是浏览器同源策略出现漏洞。

跨域通信的几种方式?

1、jsonp
script标签的异步加载

2、hash
hash改变,页面是不刷新的
(search改变,页面是会刷新的)
window.onhashchange得到window.location.hash
3、postMessage

Bwindow.postMessage('data', 'http://B.com');
windwo.addEventListener('message', function(event) {
	console.log(event.origion); // http://A.com
	console.log(event.source); // Awindow
	console.log(event.data); // data
})

4、websocket

var ws = new WebSocket('wss://echo.websocket.org');
ws.open, ws.onmessage, ws.onclose

5、cors
(1)cors会在http请求中添加origin的请求头
(2)ajax不支持跨域,cors是变种的ajax
(3)同源下,fetch就是ajax

fetch('/some/url', {
	method: 'get'
}).then(function(response) {

}).catch(function(err) {})

(4)参考资料:http://www.ruanyifeng.com/blog/2016/04/cors.html

img iframe script 来发送跨域请求有什么优缺点?

  • iframe
    优点:跨域完毕之后DOM操作和互相之间的JavaScript调用都是没有问题的
    缺点:1.若结果要以URL参数传递,这就意味着在结果数据量很大的时候需要分割传递,巨烦。2.还有一个是iframe本身带来的,母页面和iframe本身的交互本身就有安全性限制。
  • script
    优点:可以直接返回json格式的数据,方便处理
    缺点:只接受GET请求方式
  • 图片ping
    优点:可以访问任何url,一般用来进行点击追踪,做页面分析常用的方法
    缺点:不能访问响应文本,只能监听是否响应

fetch发送2次请求的原因?

fetch发送post请求的时候,总是发送2次,第一次状态码是204,第二次才成功?
原因很简单,因为你用fetch的post请求的时候,导致fetch 第一次发送了一个Options请求,询问服务器是否支持修改的请求头,如果服务器支持,则在第二次中发送真正的请求。

jsonp实现?

1、返回的是callback({});
2、

window.callback = function(data) {
	// 这是跨域得到的信息
	console.log(data);
}

可以跨域的三个标签?

<img src="xxx">
<link href="xxx">
<script src="xxx">

三个可跨域的标签的使用场景?

1、<img>用于打点统计,统计网站可能是其他域。
2、<link>,<script>可以使用cdn, cdn的也是其他域
3、<script>可以用于jsonp。


  • ajax

ajax请求的原理/ 手写一个ajax请求?

XMLHttpRequest对象

var xhr = new XMLHttpRequest();
xhr.open(method, url, false); // method:请求方式,url:请求的地址,async:是否异步请求,默认true(异步)
xhr.send(null); // xhr.send(data)
xhr.onreadtstatechange = function () {
    if (xhr.readystate == 4) {
        //响应内容解析完成,可以在客户端调用了
        if (xhr.status == 200) {
            //客户端的请求成功了
            alert(xhr.responseText);
        }
    }
}

1、延伸
(1)XMLHttpRequest对象的工作流程
(2)兼容性处理
(3)事件的触发条件
(4)事件的触发顺序

readyState?

0:(未初始化)还没有调用send()方法
1:(载入)已调用send()方法,正在发送请求
2:(载入完成)send()方法执行完成,已经接收到全部响应内容
3:(交互)正在解析响应内容
4:(完成)响应内容解析完成,可以在客户端调用了

ajax异步与同步的区别?

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

ajax传递中文用什么方法?

aSuncat:答案待完善
1、ajax配置contentType属性,加上charset=UTF-8

$.ajax({
	contentType: 'application/x-www-form-encoded;charset=utf-8'
})

2、提交前采用encodeURI两次编码(encodeURI取代了escape)

$.ajax({
	data: {
		id: 1,
		userName: encodeURI(encodeURI('小商品'))
	}
})

3、对参数进行encodeURI(不知道正不正确,需验证。是不是也得是2次)

$.ajax({
	data: {
		arr: encodeURI(arr)
	}
})

  • 错误监控

前端错误的分类/ 如何检测js错误/ 如何保证你的产品质量?

1、即时运行错误:代码错误
(1)try.catch
(2)window.onerror:只能捕获即时运行错误
2、资源加载错误,资源加载错误不会向上冒泡,不会冒泡到window
(1)object.onerror:图片有onerror事件,script标签也有onerror事件
(2)performance.getEntries:高级浏览器,可以间接拿到没有加载的资源错误。返回的是一个数组。

performance.getEntries().forEach(item=> {console.log(item.name)}); // 打印出来的是已成功加载的资源
document.getElementByTagName('img'); // 打印出来的是所有图片资源(包括已经成功加载和没有成功加载)

3、error事件捕获:资源加载错误不会冒泡,但是会发生捕获

错误的捕获方式?

同上述方法3的error事件捕获

window.addEventListener('error', function(e) {
	console.log('捕获:', e);
}, true); // 第三个参数是true:捕获, false:冒泡

1、延伸:
跨域的js运行错误可以捕获吗,错误提示什么,应该怎么处理?
(1)在script标签增加crossorigin属性。
(2)设置js资源响应头Access-Control-Allow-Origin:*

上报错误的基本原理?

1、采用ajax通信的方式上报。
能做到,但是所有的错误监控都不是通过这种方式做的。
2、采用image对象上报。
所有的错误方式都是通过这种方式上报的。

(new Image()).src = 'http://baidu.com/tesjk?r=tksjk'; // 不需要借助任何第三方库,错误上报路径后面可以加任何参数,如r=tksjk等

参考资料:http://www.cnblogs.com/luozhihao/p/8635507.html


  • 模块化

一、模块化
1、amd
AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
(1)require.js

<script src="/require.min.js" data-main="./main.js"></script>
<!--main.js是入口-->

(2)全局define函数

define(['./a-util.js'], function(aUtil) {
	return {
		printDate: function(date) {
			console.log(aUtil.aGetFromatDate(date));
		}
	}
})

(3)全局require函数

// main.js
require(['./a.js'], function(a) {
	var date = new Date();
	a.printDate(date);
})

(4)依赖js会自动、异步加载
2、cmd
CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。

3、commonJS

define(function(require, exports,module) {
      // 模块代码
});

amd、cmd区别?

1、依赖
amd推崇依赖前置。amd在定义模块的时候要先声明其依赖的模块。
cmd推崇就近依赖。cmd只要依赖的模块在附近就行了。

amd、commonJs区别?

1、模块对象输出
amd推荐的风格通过返回一个对象做为模块对象
CommonJS的风格通过对module.exports或exports的属性赋值来达到暴露模块对象的目的。


  • 虚拟dom

1、vdom,virtual/ˈvɝ​tʃʊəl/ dom,虚拟dom。
用js模拟dom结构,提高重绘性能。

vdom的如何应用,核心api是什么?

1、以snabbdom(开源vdom库)为例:
(1)①h函数:生成dom节点

h('<标签名>', {...属性...}, [...子元素...])
h('<函数名>', {...属性...}, '...')

②patch函数:进行对比,进行打补丁渲染

patch(container, vnode)
patch(vnode, newVnode)

(2)

var newVnode = h('table', {}, data.map(function(item) {
	var tds = [];
	var i;
	for (i in item) {
		if (item.hasOwnProperty(i)) {
			tds.push(h('td', {}, item[i] + ''));
		}
	}
	return h('tr', {}, tds);
}))

虚拟dom转换成真实dom?

1、没有旧节点

function createElement(vnode) {
	var tag = vnode.tag; // 'ul'
	var attrs = vnode.attrs || {};
	var children = vnode.children || [];
	if (!tag) {
		return null;
	}
	// 创建真实的dom元素
	var elem = document.createElement(tag);
	// 属性
	var attrName;
	for (attrName in attrs) {
		if (attrs.hasOwnProperty(attrName)) { // hasOwnProperty:true-是自己的属性,而不是原型property中的属性
			elem.setAttribute(attrNam, attrs[attrName]);
		}
	}
	// 子元素
	children.forEach(function(function(childVnode)){
		// 给elem添加子元素
		elem.appendChild(createElement(childVnode));
	})
	// 返回真实的dom元素
	return elem;
}

diff实现过程?

patch(container, vnode) 和patch(vnode, newVnode);
createElement
updateChildren

  • 深拷贝/ 浅拷贝

深拷贝和浅拷贝,分别举例?

待完善


  • js基础

1、常说的js(浏览器执行的js)包含两部分
(1)js基础知识:ECMA 262标准(类型、原型、作用域、异步)
(2)JS-WEB-API:W3C标准- w3c标准没有规定任何js基础相关的东西,只管定义用于浏览器中js操作页面的api和全局变量

对js的理解?

js是一种基于对象和事件驱动,并具有安全性的脚本语言。

请说出以下代码输出的值?

1、

for (var i = 0; i < 5; i++) {
    console.log(i);
  }

输出0-4
2、

 for (let i = 0; i < 5; i++) {
    setTimeout(function () {
      console.log(i);
    }, 1000 * i);
  }

(1)var:每隔1s输出一个5,共输出5个;let:0-4,每隔1s输出一个数字
(2)setTimeout延迟执行,执行到console.log()的时候,i已经变成5了。
(3)let的作用范围是块作用域。
3、

for (var i = 0; i < 5; i++) {
    (function (i) {
      setTimeout(function () {
        console.log(i);
      }, i * 1000);
    })(i);
  }

(1)0-4,每隔1s输出一个数字
(2)闭包
4、

for (var i = 0; i < 5; i++) {
    (function () {
      setTimeout(function () {
        console.log(i);
      }, i * 1000);
    })(i);
  }

(1)var:每隔1s输出一个5,共输出5个;let:0-4,每隔1s输出一个数字
(2)var:内部其实没有对 i 保持引用,会变成输出 5;
5、

 for (let i = 0; i < 5; i++) {
    setTimeout((function (i) {
      console.log(i);
    })(i), i * 1000);
  }

(1)0-4,每隔1s输出一个数字
(2)这里给 setTimeout 传递了一个立即执行函数。setTimeout 可以接受函数或者字符串作为参数,那么这里立即执行函数应该是个 undefined ,也就是说等价于:setTimeout(undefined, ...);
而立即执行函数会立即执行,那么应该是立马输出的。
6、

setTimeout(function() {
  console.log(1)
}, 0);
new Promise(function executor(resolve) {
  console.log(2);
  for( var i=0 ; i<10000 ; i++ ) {
    i == 9999 && resolve();
  }
  console.log(3);
}).then(function() {
  console.log(4);
});
console.log(5);

(1)依次输出2、3、5、4、1
(2)①setTimeout,于是会先设置一个定时,在定时结束后将传递这个函数放到任务队列里面,因此开始肯定不会输出 1 。
②然后是一个 Promise,里面的函数是直接执行的,因此应该直接输出 2 3 。
③因此,应当先输出 5,然后再输出 4 。
④最后在到下一个 tick,就是 1 。

把以下代码,改写成依次输出0-9

var funcs = []
for (var i = 0; i < 10; i++) {
  funcs.push(function () {
    console.log(i)
  })
}
funcs.forEach(function (func) {
  func(); //输出十个10
})

1、let

var funcs = []
for (let i = 0; i < 10; i++) {
  funcs.push(function () {
    console.log(i)
  })
}
funcs.forEach(function (func) {
  func(); //依次输出0-9
})

2、立即执行函数

var funcs = []
for (var i = 0; i < 10; i++) {
  funcs.push((function (value) {
    return function () {
      console.log(value)
    }
  }(i)))
}
funcs.forEach(function (func) {
  func(); //依次输出0-9
})

3、闭包

function show(i) {
  return function () {
    console.log(i)
  }
}
var funcs = []
for (var i = 0; i < 10; i++) {
  funcs.push(show(i))
}
funcs.forEach(function (func) {
  func(); //0 1 2 3 4 5 6 7 8 9
})

如何区分数组对象,普通对象,函数对象

1、数组对象

var isArray = Array.isArray || function(obj) {
	return Object.prototype.toString.call(obj) === '[object Array]';
}

2、函数

var isFunction = function(obj) {
	return Object.prototype.toString.call(obj) === '[object Function]';
}
if(typeof /./ != 'function' && typeof Int8Array != 'object') {
	isFunction = function(obj) {
		return typeof obj == 'function';
	}
}

(1)简单且性能最好的办法是typeof obj == ‘function’,但是在某些浏览器存在bug(这些浏览器在对正则表达式使用typeof的时候,不会返回’object‘而是’function’,对Int8Array使用typeof时,不会返回’function‘而是’object‘)。
(2)ie9及以上版本,才能用typeof或Object.prototype.toString(),而在IE678中,都返回object而不是function。
3、普通对象

var isObject = function(obj) {
	var type = typeof obj;
	return type === 'object && !!obj;
}

要排除null值的情况,因为typeof null 得到的也是 ‘object’。

fetch和xhr的区别

待完善

面向对象、面向过程

1、面向对象:将你的需求抽象成一个对象,然后针对这个对象分析其特征(属性)与动作(方法),这个对象就称之为类。
2、面向过程:将你需要的功能放在一个对象里面。

面向对象的三大基本特性

封装、继承、多态

XML和JSON的区别?

1、数据体积方面
JSON相对于XML来讲,数据的体积小,传递的速度更快些。
2、数据交互方面
JSON与JavaScript的交互更加方便,更容易解析处理,更好的数据交互。
3、数据描述方面
JSON对数据的描述性比XML较差。
4、传输速度方面
JSON的速度要远远快于XML。

Web Worker 和webSocket?

1、worker主线程:
(1)通过 worker = new Worker( url ) 加载一个JS文件来创建一个worker,同时返回一个worker实例。
(2)通过worker.postMessage( data) 方法来向worker发送数据。
(3)绑定worker.onmessage方法来接收worker发送过来的数据。
(4)可以使用 worker.terminate() 来终止一个worker的执行。
2、WebSocket
是Web应用程序的传输协议,它提供了双向的,按序到达的数据流。他是一个Html5协议,WebSocket的连接是持久的,他通过在客户端和服务器之间保持双工连接,服务器的更新可以被及时推送给客户端,而不需要客户端以一定时间间隔去轮询。

Javascript垃圾回收方法?

1、标记清除(mark and sweep)
这是JavaScript最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。
垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了
2、引用计数(reference counting)
在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时 候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。
在IE中虽然JavaScript对象通过标记清除的方式进行垃圾回收,但BOM与DOM对象却是通过引用计数回收垃圾的,
也就是说只要涉及BOM及DOM就会出现循环引用问题。

new操作符具体干了什么呢?

1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、新创建的对象由 this 所引用,并且最后隐式的返回 this 。

var obj  = {};
obj.__proto__ = Base.prototype;
Base.call(obj);

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

defer和async、动态创建DOM方式(创建script,插入到DOM中,加载完毕后callBack)、按需异步载入js

WEB应用从服务器主动推送Data到客户端有那些方式?

1、Javascript数据推送
2、Commet:基于HTTP长连接的服务器推送技术
3、 基于WebSocket的推送方案
4、SSE(Server-Send Event):服务器推送数据新方式

首屏和白屏时间如何计算?

首屏时间的计算,可以由 Native WebView 提供的类似 onload 的方法实现,在 ios 下对应的是 webViewDidFinishLoad,在 android 下对应的是onPageFinished事件。
白屏的定义有多种。可以认为“没有任何内容”是白屏,可以认为“网络或服务异常”是白屏,可以认为“数据加载中”是白屏,可以认为“图片加载不出来”是白屏。场景不同,白屏的计算方式就不相同。
方法1:当页面的元素数小于x时,则认为页面白屏。比如“没有任何内容”,可以获取页面的DOM节点数,判断DOM节点数少于某个阈值X,则认为白屏。
方法2:当页面出现业务定义的错误码时,则认为是白屏。比如“网络或服务异常”。
方法3:当页面出现业务定义的特征值时,则认为是白屏。比如“数据加载中”。

简述一下前端事件流?

参考答案
HTML中与javascript交互是通过事件驱动来实现的,例如鼠标点击事件onclick、页面的滚动事件onscroll等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。
什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM2级事件流包括下面几个阶段。
事件捕获阶段
处于目标阶段
事件冒泡阶段
addEventListener:addEventListener是DOM2 级事件新增的指定事件处理程序的操作,这个方法接收3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
IE只支持事件冒泡。

  • 49
    点赞
  • 198
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值