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
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、字面量重写原型
一是字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。
2、借用构造函数(类式继承)
借用构造函数虽然解决了刚才两种问题,但没有原型,则复用无从谈起。所以我们需要原型链+借用构造函数的模式,这种模式称为组合继承
3、组合式继承
组合式继承是比较常用的一种继承方法,其背后的思路是 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性。
延伸:
一个对象,是继承了某个类,问你他的原型链?
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报错
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
(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? |
事件循环。
1、js实现异步的具体解决方案:event-loop
2、运行栈:执行同步任务的
3、浏览器js引擎遇到了setTimeout,识别了这是一个异步任务,不会将其放入运行栈,而是把它拿走,拿走了之后也没有立马放到异步任务队列中,按延迟时间放入到任务队列中。同步任务没有正在执行的东西,就会读异步任务,把任务放到运行栈中,执行完了又去读异步任务,把任务放到运行栈中,如此循环。
event-loop流程? |
1、同步代码,直接执行
2、异步先放在任务队列中
3、待同步函数执行完毕,轮询执行任务队列的函数
哪些语句会放入异步任务队列中? |
1、定时任务:setTimeout、setInterval
2、网络请求:ajax请求、动态加载
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中的函数放入到异步队列中
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的区别? |
undefined的典型用法? |
null的典型用法? |
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中有哪些内置函数/ 数据封装类对象? |
内置函数:
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 '' + item + '';
})
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
以上方法中之所以给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 ]
一、 || 判断
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)解决:
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事件的级别? |
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属性
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父子组件之间的通信方式? |
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);
}
可以跨域的三个标签? |
(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的属性赋值来达到暴露模块对象的目的。
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
深拷贝和浅拷贝,分别举例? |
待完善
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):服务器推送数据新方式