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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/aSuncat/article/details/88789888

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


  • 原型链

创建对象有几种方法?

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、实例本身的属性和方法如果没有找到,就会去找原型对象的属性和方法。如果在某一级找到了,就会停止查找,并返回结果

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后面的()可以不要

  • 继承

继承的本质是原型链

call、apply的共同点与区别?

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

用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、js没有块级作用域,有函数作用域、全局作用域。
es6出现才有块级作用域。

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、闭包是函数和声明该函数的词法环境的组合。

闭包应用场景?

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

  • 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:一个只有一个值的特殊类型。表示一个空对象引用。
undefined:一个没有设置值的变量。
2、null 和 undefined 的值相等,但类型不等:

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的类型相同,但是值不同。


  • 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

数组属性:
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


  • 内存泄漏

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、新增节点:div1.appendChild();
2、获取父节点:var parent = div1.parentElement
3、获取子节点:var child= div1.childNode
4、删除节点:div1.removeChild(child[0])

  • dom事件

dom事件的级别?

1、dom0 element.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();

  • 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

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
(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、commonJS

amd、cmd区别?

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


  • 虚拟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、面向过程:将你需要的功能放在一个对象里面。

面向对象的三大基本特性

封装、继承、多态

猜你喜欢

转载自blog.csdn.net/aSuncat/article/details/88789888