共计 25440 个字符,预计需要花费 64 分钟才能阅读完成。
1. 实用 js 写红绿灯的成果?
<ul id="traffic" class="">
<li id="green"></li>
<li id="yellow"></li>
<li id="red"></li>
</ul>
ul {
position: absolute;
width: 200px;
height: 200px;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
/* 画 3 个圆代表红绿灯 */
ul >li {
width: 40px;
height: 40px;
border-radius:50%;
opacity: 0.2;
display: inline-block;
}
/* 执行时扭转透明度 */
ul.red >#red,
ul.green >#green,
ul.yellow >#yellow{opacity: 1.0;}
/* 红绿灯的三个色彩 */
#red {background: red;}
#yellow {background: yellow;}
#green {background: green;}
function timeout(timer){return function(){return new Promise(function(resolve,reject){setTimeout(resolve,timer)
})
}
}
var green = timeout(3000);
var yellow = timeout(4000);
var red = timeout(5000);
var traffic = document.getElementById("traffic");
(function restart(){
'use strict' // 严格模式
console.log("绿灯"+new Date().getSeconds()) // 绿灯执行三秒
traffic.className = 'green';
green()
.then(function(){console.log("黄灯"+new Date().getSeconds()) // 黄灯执行四秒
traffic.className = 'yellow';
return yellow();})
.then(function(){console.log("红灯"+new Date().getSeconds()) // 红灯执行五秒
traffic.className = 'red';
return red();}).then(function(){restart()
})
})();
2. axios 是否须要 promise 封装?
须要
import axios from 'axios'
const http = ({url,method,params,headers}) => {return new Promise ( (resolve,reject) => {
axios({
url,
method,
params,
headers
})
.then( res => {resolve(res)
})
.catch( error => {throw error})
})
}
export default http
3. Promise 外部产生谬误,如果同时.then 办法有第二个参数,也有.catch 会调用哪个
.catch
4. 宏工作 微工作
setTimeout(function(){console.log('1')
});
new Promise(function(resolve){console.log('2');
resolve();}).then(function(){console.log('3')
});
console.log('4')
settimeout 必定是异步的。我也晓得有一个 event 队列,你 settimeout 没设置工夫应该间接就进入这个队列了吧,而后就是 Promise 的回掉函数进入 event 队列。过后我二话不说给了个答案 2,4,1,3. 并且很自信。而后面试官就问你不想想了? 我说不想了。而后后半段他全程开始皱眉头了。我也凉凉。最初他让我回去看一下宏工作和微工作。
首先说一下一般的异步函数的执行过程吧
同步和异步工作别离进入不同的执行 ” 场合 ”,同步的进入主线程,异步的进入 Event Table 并注册函数。当指定的事件实现时,Event Table 会将这个函数移入 Event Queue。主线程内的工作执行结束为空,会去 Event Queue 读取对应的函数,进入主线程执行。上述过程会一直反复,也就是常说的 Event Loop(事件循环)。
那么如此看来我给的答案还是对的。然而 js 异步有一个机制,就是遇到宏工作,先执行宏工作,将宏工作放入 eventqueue,而后在执行微工作,将微工作放入 eventqueue 最骚的是,这两个 queue 不是一个 queue。当你往外拿的时候先从微工作里拿这个回掉函数,而后再从宏工作的 queue 上拿宏工作的回掉函数。我过后看到这我就服了还有这种骚操作。
而宏工作个别是:包含整体代码 script,setTimeout,setInterval。
微工作:Promise,process.nextTick。
记住就行了。
而后回到结尾的代码。因为 settimeout 是宏工作,尽管先执行的他,然而他被放到了宏工作的 eventqueue 外面,而后代码持续往下查看看有没有微工作,检测到 Promise 的 then 函数把他放入了微工作序列。等到主线过程的所有代码执行完结后。先从微工作 queue 里拿回掉函数,而后微工作 queue 空了后再从宏工作的 queue 拿函数。
所以正确的执行后果当然是:2,4,3,1。
https://juejin.im/post/59e85e…
5. Js 原型和原型链
原型链的设计是 js 的精华所在,比拟形象。须要从外部设计原理去了解这种设计思维,在纸上画画其中的关系会帮忙了解。
prototype 对象
prototype 对象的引入:所有实例对象须要共享的属性和办法,都放在这个对象中,那些不须要共享的属性和办法,就放在构造函数中。以此来模仿类。
function Animal(name) {this.name = name}
Animal.prototype.getName = function() {console.log(this.name)
}
var animal1 = new Animal('Kate')
var animal2 = new Animal('Lucy')
// 对象 animal1 和 animal2 共享方法 getName
animal1.getName()
animal2.getName()
原型链
在 javascript 中,每个对象都有一个指向它的 原型 (prototype) 对象 的外部链接。每个原型对象又有本人的原型,直到某个对象的原型为 null 为止,组成这条链的最初一环。
*proto写入 es6 规范
当一个对象被创立时,它的 __protp__
属性和外部属性 [[prototype]]
指向雷同的对象(也就是它的构造函数的 prototype
属性)。扭转 __proto__
属性的值同时也会扭转外部属性 [[prototype]]
的值,除非该对象是不可扩大的。
在 ES5 中,所有构造函数的__proto__都指向 Function.prototype
** 在 ES6 中,构造函数的__proto__指向它的父类构造函数
obj.__proto__ === obj.[[prototype]]
// ES5
Cat.__proto__ === Function.prototype
// ES6
Cat.__proto__ === Animal
构造函数继承
有四种形式能够实现构造函数的继承
1. 调用 apply 办法
function Animal() {this.species = '动物'}
Animal.prototype.getName = function() {console.log('我是动物')
}
function Cat() {Animal.apply(this, arguments)
}
var cat = new Cat()
cat.species // 动物
cat.getName() // undefined
这种办法能够继承父类构造函数的属性,然而无奈继承 prototype
属性,即父类中共享的办法和属性
2. 改写 prototype
对象
Cat.prototype = new Animal()
Cat.prototype.constructor = Cat
这是最罕用的办法来模仿单继承,毛病是始终要保留 Animal 的对象,如果 Animal 对象比拟大时,会耗费局部内存(其实很少),并且没有实现多继承
3. 间接继承prototype
Cat.prototype = Animal.prototype
Cat.prototype.constructor = Cat
毛病是当批改了 Cat.prototype 上的办法时会影响 Animal.prototype
4. 利用空对象作中介
var F = function(){}
F.prototype = Animal.prototype
Cat.prototype = new F()
Cat.prototype.constructor = Cat
6. js 的事件循环机制是什么
过程、线程
- 过程是零碎调配的独立资源,是 CPU 资源分配的根本单位,过程是由一个或者多个线程组成的。
- 线程是过程的执行流,是 CPU 调度和分派的根本单位,同个过程之中的多个线程之间是共享该过程的资源的。
浏览器内核
- 浏览器是多过程的,浏览器每一个 tab 标签都代表一个独立的过程(也不肯定,因为多个空白 tab 标签会合并成一个过程),浏览器内核(浏览器渲染过程)属于浏览器多过程中的一种。
-
浏览器内核有多种线程在工作。
-
GUI 渲染线程:
- 负责渲染页面,解析 HTML,CSS 形成 DOM 树等,当页面重绘或者因为某种操作引起回流都会调起该线程。
- 和 JS 引擎线程是互斥的,当 JS 引擎线程在工作的时候,GUI 渲染线程会被挂起,GUI 更新被放入在 JS 工作队列中,期待 JS 引擎线程闲暇的时候继续执行。
-
JS 引擎线程:
- 单线程工作,负责解析运行 JavaScript 脚本。
- 和 GUI 渲染线程互斥,JS 运行耗时过长就会导致页面阻塞。
-
事件触发线程:
- 当事件合乎触发条件被触发时,该线程会把对应的事件回调函数增加到工作队列的队尾,期待 JS 引擎解决。
-
定时器触发线程:
- 浏览器定时计数器并不是由 JS 引擎计数的,阻塞会导致计时不精确。
- 开启定时器触发线程来计时并触发计时,计时实现后会被增加到工作队列中,期待 JS 引擎解决。
-
http 申请线程:
- http 申请的时候会开启一条申请线程。
- 申请实现有后果了之后,将申请的回调函数增加到工作队列中,期待 JS 引擎解决。
-
JavaScript 引擎是单线程
JavaScript 引擎是单线程,也就是说每次只能执行一项工作,其余工作都得依照程序排队期待被执行,只有以后的工作执行实现之后才会往下执行下一个工作。
HTML5 中提出了 Web-Worker API,次要是为了解决页面阻塞问题,然而并没有扭转 JavaScript 是单线程的实质。理解 Web-Worker。
JavaScript 事件循环机制
JavaScript 事件循环机制分为浏览器和 Node 事件循环机制,两者的实现技术不一样,浏览器 Event Loop 是 HTML 中定义的标准,Node Event Loop 是由 libuv 库实现。这里次要讲的是浏览器局部。
Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的工作都会被放到调用栈期待主线程执行。
-
JS 调用栈
JS 调用栈是一种后进先出的数据结构。当函数被调用时,会被增加到栈中的顶部,执行实现之后就从栈顶部移出该函数,直到栈内被清空。
-
同步工作、异步工作
JavaScript 单线程中的工作分为同步工作和异步工作。同步工作会在调用栈中依照程序排队期待主线程执行,异步工作则会在异步有了后果后将注册的回调函数增加到工作队列 (音讯队列) 中期待主线程闲暇的时候,也就是栈内被清空的时候,被读取到栈中期待主线程执行。工作队列是先进先出的数据结构。
-
Event Loop
调用栈中的同步工作都执行结束,栈内被清空了,就代表主线程闲暇了,这个时候就会去工作队列中依照程序读取一个工作放入到栈中执行。每次栈内被清空,都会去读取工作队列有没有工作,有就读取执行,始终循环读取 - 执行的操作,就造成了事件循环。
-
-
7. 前端跨域的形式
前端跨域的计划:1、通过 jsonp 跨域
2、postMessage 跨域
3、跨域资源共享(CORS)4、nginx 代理跨域
5、nodejs 中间件代理跨域
6、WebSocket 协定跨域
7. 反向代理
<u>https://segmentfault.com/a/1190000011145364</u>
8.Promise 的了解,和 promise 都有哪些办法
Promise,就是一个对象,用来传递异步操作的音讯,防止了层层嵌套的回调函数。它代表了某个将来才会晓得后果的事件(通常是一个异步操作),并且这个事件提供对立的 API,可供进一步解决。(1)对象的状态不受外界影响。有三种状态:Pending(进行中)、Resolved(已实现,又称 Fulfilled)和 Rejected(已失败)。(2)一旦状态扭转,就不会再变,任何时候都能够失去这个后果。Promise 对象的状态扭转,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只有这两种状况产生,状态就凝固了,不会再变了,会始终放弃这个后果
9. 原型和原型链的了解
咱们发明的每一个函数都有一个 prototype(原型)属性。这个属性是一个指针,指向原型对 象。在默认状况下,所有的原型对象都会有一个 constructor(构造函数)属性,这个属性蕴含一个指向 prototype 属相所在的指针。当调用构造函数创立一个新实例之后,该实例外部将蕴含一个指针(外部属性),指向构造函数的原型对象。<u>https://juejin.im/post/5ae95290518825672c00c0a4</u>
10: 异步形式
1. Promise
2. Generator
3. Async-await
4. Node.js 中的 nextTick()和 setimmediate()
5. async 库
11: 修复 bug 或增加新性能的常见工作流(git 命令)是什么?空和未定义的 javascript 有什么区别?
git flow feature start f1 增加新个性,这个操作创立了一个基于 develop 的个性分支,并切换到这个分支之下。git flow feature finish f1 实现新个性,这个操作会合并 f1 分支到 develop 分支,并删除个性分支,切换回 develop 分支。git flow feature publish f1 公布新分支,公布新个性分支到近程服务器,其它用户也能够应用这分支。修复 bug:git flow hotfix start VERSION [BASENAME] 创立 hotfix 分支,VERSION 参数标记着修改版本,[BASENAME]为 finish release 时填写的版本号。
12: 您应用什么框架来编写单元测试,写下一个案例来验证调用的函数
一、问题形容:在一个升序数组中,应用折半查找失去要查问的值的索引地位。如:
var a=[1,2,3,4,5,6,7,8,9];
search(a,3);// 返回 2
search(a,1);// 左边界,返回 0
search(a,9);// 右边界,返回 8
search(a,0);// 比最小的值还小,返回 "您查找的数值不存在"
search(a,10);// 比最大的值还大,返回 "您查找的数值不存在"
注:折半查找必须在有序数组中才无效,无序的数组不能实现查找性能。比方:在 [10,5,6,7,8,9,20] 中查找 10,两头索引地位的值为 7,比拟得出 7 比 10 小,因此应该在右子数组中查找,实际上不可能找到 10;
二、我的实现
function search(arr,num) {
var l=arr.length;
var left=0;
var right=l-1;
var center=Math.floor((left+right)/2);
while(left<=l-1&&right>=0){if (arr[center]==num) return center;
if (left==right) return "您查找的数不存在";
if (arr[center]>num) {
right=center-1;
center=Math.floor((left+right)/2);
}else if (arr[center]<num) {
left=center+1;
center=Math.floor((left+right)/2);
}
}
}
var a=[1,2,3,4,5,6,7,8,9];
console.log(search(a,-2));
阐明:1、基本思路:每次比拟,如果数组两头索引地位的值比要查找的值大,就转而在数组两头地位之前的子数组中查找;相同,如果数组两头索引地位的值比要查找的值大,就转而在数组两头地位之后的子数组中查找;如果数组两头索引地位的值恰好等于要查找的值,就返回该索引地位。2、left 定义查找范畴的起始地位,right 定义查找范畴的完结地位,center 定义查找范畴的两头地位。3、while 中的逻辑阐明:(1)因为不晓得具体查找查找多少次,while 是比拟好的抉择;(2)循环完结条件:a、一旦当 right 小于 0 时,就不再查找,再纠缠也不会有后果。例如:在 a =[1,2,3,4,5,6,7,8,9]中查找 0,当查找范畴变为 left=0,right=0,center= 0 时,进入 while 语句,因为 arr[center]>0, 故执行
right=center-1;center=Math.floor((left+right)/2);
失去 right=- 1 此时应不再进入循环;b、一旦当 left>l- 1 时,就不再查找,同样再纠缠也不会有后果。例如:在 a =[1,2,3,4,5,6,7,8,9]中查找 10,当查找范畴变为 left=8,right=8,center= 8 时,进入 while 语句,因为 arr[center]<10, 故执行
left=center;center=Math.floor((left+right)/2);
失去 left=9, 此时应不再进入循环;4、始终是通过 center 匹配到要查找的值;5、Math.floor 解决了查找范畴长度为偶数的状况;6、当 left==right 了,而 arr[center]==num 却没执行,能够得出结论查找不到的;7、当 arr[center]==num 时,整个函数都完结了,前面语句是不会执行的。
13. 编写一个 regex 表达式以查找内容,内容以 2 个数字结尾,以结尾
var reg = /^0-9$/
14.push 增加数组后,是怎么响应的
push() 办法可向数组的开端增加一个或多个元素,并返回新的长度
正文:该办法会扭转数组的长度。
语法:
arrayObject.push(newelement1,newelement2,….,newelementX)
参数形容
newelement1 必须。要增加到数组的第一个元素。
newelement2 可选。要增加到数组的第二个元素。
newelementX 可选。可增加多个元素。
push() 办法可把它的参数程序增加到 arrayObject 的尾部。它间接批改 arrayObject,而不是创立一个新的数组。push() 办法和 pop() 办法应用数组提供的先进后出栈的性能。
15.js 根本数据类型
Undefined、Null、Bollean、Number、String
16.js 中 == 和 === 的区别是什么
前者会主动转换类型
后者不会
17.for 和 for in 区别
语法结构上不同,
for 个别用来遍历数组的,是比较简单的操作
for in 个别用来遍历对象,尽管 for in 也能遍历数组,然而会存在
以下几个问题:
1、index 索引为字符串型数字,不能间接进行几何运算
2、遍历程序有可能不是依照理论数组的外部程序
3、应用 for in 会遍历数组所有的可枚举属性,包含原型。例如上栗
的原型办法 method 和 name 属性
这也是为什么用 for 不必 for in 的区别,如果是遍历一般数组的话,
用 for 是最好的抉择,然而如果是对象,用 for in 就好了。
18.js 中 == 和 === 的区别是什么
操作数 1 == 操作数 2,操作数 1 === 操作数 2
双等号 ==:(1)如果两个值类型雷同,再进行三个等号 (===) 的比拟(2)如果两个值类型不同,也有可能相等,需依据以下规定进行类型转换在比拟:1)如果一个是 null,一个是 undefined,那么相等
2)如果一个是字符串,一个是数值,把字符串转换成数值之后再进行比拟
三等号 ===:(1)如果类型不同,就肯定不相等(2)如果两个都是数值,并且是同一个值,那么相等;如果其中至多一个是 NaN,那么不相 等。(判断一个值是否是 NaN,只能应用 isNaN( ) 来判断)(3)如果两个都是字符串,每个地位的字符都一样,那么相等,否则不相等。(4)如果两个值都是 true,或是 false,那么相等(5)如果两个值都援用同一个对象或是函数,那么相等,否则不相等(6)如果两个值都是 null,或是 undefined,那么相等
19:for 和 for in 区别
for in:
1.for...in 语句用于对数组或者对象的属性进行循环操作。2.for ... in 循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作。3.for...in 语句以任意程序遍历一个对象的可枚举属性。对于每个不同的属性,语句都会被执行。for :
1.for 循环是对数组的元素进行循环,而不能引用于非数组对象。
20: 数组去重的办法
第一种:function uniq(array){var temp = []; // 一个新的长期数组
for(var i = 0; i < array.length; i++){if(temp.indexOf(array[i]) == -1){temp.push(array[i]);
}
}
return temp;
}
var aa = [1,2,2,4,9,6,7,5,2,3,5,6,5];
console.log(uniq(aa));
第二种:对象键值法去重
function uniq(array){var temp = {}, r = [], len = array.length, val, type;
for (var i = 0; i < len; i++) {val = array[i];
type = typeof val;
if (!temp[val]) {temp[val] = [type];
r.push(val);
} else if (temp[val].indexOf(type) < 0) {temp[val].push(type);
r.push(val);
}
}
return r;
}
var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));
第三种:排序后相邻去除法
function uniq(array){array.sort();
var temp=[array[0]];
for(var i = 1; i < array.length; i++){if( array[i] !== temp[temp.length-1]){temp.push(array[i]);
}
}
return temp;
}
var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));
第四种:数组下标法
function uniq(array){var temp = [];
for(var i = 0; i < array.length; i++) {
// 如果以后数组的第 i 项在以后数组中第一次呈现的地位是 i,才存入数组;否则代表是反复的
if(array.indexOf(array[i]) == i){temp.push(array[i])
}
}
return temp;
}
var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));
第五种:优化遍历数组法
function uniq(array){var temp = [];
var index = [];
var l = array.length;
for(var i = 0; i < l; i++) {for(var j = i + 1; j < l; j++){if (array[i] === array[j]){
i++;
j = i;
}
}
temp.push(array[i]);
index.push(i);
}
console.log(index);
return temp;
}
var aa = [1,2,2,3,5,3,6,5];
console.log(uniq(aa));
21:排序的办法
第一种:冒泡排序:var arr = [1,4,-8,-3,6,12,9,8];
function bubbleSort(arr){for (var i = 0; i < arr.length; i++) {for (var j = 0; j < arr.length-i-1; j++) {if(arr[j] > arr[j+1]){var c = arr[j];
arr[j] = arr[j+1];
arr[j+1] = c;
}
}
}
return arr;
}
console.log(bubbleSort(arr));
疾速排序:var arr = [1,4,-8,-3,6,12,9,8];
function quicksort(arr){if(arr.length <= 1){return arr;}
var middleIndex = Math.floor(arr.length/2);
var middleNum = arr.splice(middleIndex,1);
var left = [], right = [];
for (var i = 0; i < arr.length; i++) {if(arr[i] < middleNum){left.push(arr[i]);
} else {right.push(arr[i]);
}
}
return quicksort(left).concat(middleNum, quicksort(right));
}
console.log(quicksort(arr));
抉择排序:var arr = [1,4,-8,-3,6,12,9,8];
function selectSort(arr){for(var i=0;i<arr.length;i++){
// 设置以后范畴最小值和索引
var min = arr[i];
var minIndex = i;
// 在该范畴选出最小值
for(var j=i+1;j<arr.length;j++){if(min>arr[j]){min = arr[j];
minIndex = j;
}
}
// 将最小值插入, 并将原来地位的最小值删除
arr.splice(i,0,min);
arr.splice(minIndex+1,1);
}
return arr;
}
console.log(selectSort(arr));
插入排序:var array = [1,4,-8,-3,6,12,9,8];
function selectSort(arr){for(var i=0;i<arr.length;i++){
// 设置以后范畴最小值和索引
var min = arr[i];
var minIndex = i;
// 在该范畴选出最小值
for(var j=i+1;j<arr.length;j++){if(min>arr[j]){min = arr[j];
minIndex = j;
}
}
// 将最小值插入, 并将原来地位的最小值删除
arr.splice(i,0,min);
arr.splice(minIndex+1,1);
}
}
selectSort(array);
document.write(array);
22:冒泡排序
var arr = [1,4,-8,-3,6,12,9,8];
function bubbleSort(arr){for (var i = 0; i < arr.length; i++) {for (var j = 0; j < arr.length-i-1; j++) {if(arr[j] > arr[j+1]){var c = arr[j];
arr[j] = arr[j+1];
arr[j+1] = c;
}
}
}
return arr;
}
console.log(bubbleSort(arr));
23: 原型链的了解:
在谈原型链之前,咱们首先要理解自定义函数与 Function 之间是什么关系,而构造函数、原型和实例之间又存在什么千头万绪的关系呢?其实,所有的函数都是 Function 的实例。在构造函数上都有一个原型属性 prototype,该属性也是一个对象;那么在原型对象上有一个 constructor 属性,该属性指向的就是构造函数;而实例对象上有一个 _proto_ 属性,该属性也指向原型对象,并且该属性不是规范属性,不能够用在编程中,该属性用于浏览器外部应用。
[外链图片转存失败, 源站可能有防盗链机制, 倡议将图片保留下来间接上传(img-QqhVpWo7-1630416218424)(en-resource://database/450:1)]
[外链图片转存失败, 源站可能有防盗链机制, 倡议将图片保留下来间接上传(img-8tp8hnPS-1630416218430)(en-resource://database/452:1)]
1、原型链
1)构造函数、原型和实例的关系
①构造函数都有一个属性 prototype,这个属性是一个对象(Object 的实例)②原型对象 prototype 外面有一个 constructor 属性,该属性指向原型对象所属的构造函数
③实例对象都有一个_proto_属性,该属性也指向构造函数的原型对象,它是一个非标准属性,不能够用于编程,它是用于浏览器本人应用的
2)prototype 与_proto_的关系
①prototype 是构造函数的属性
②_proto_是实例对象的属性 ——这两者都指向同一个对象【总结】i)函数也是对象,对象不肯定是函数;ii)对象的实质:无序的键值对汇合;键值对当中的值能够是任意数据类型的值
iii)对象就是一个容器,这个容器当中放的是(属性和办法)3)属性搜寻
①在拜访对象的某个成员的时候会先在对象中找是否存在
②如果以后对象中没有就在构造函数的原型对象中找
③如果原型对象中没有找到就到原型对象的原型上找
④晓得 Object 的原型对象的原型是 null 为止
2、Function——
所有函数都是 Function 的实例
`①本地对象:独立于宿主环境(浏览器)的对象——包含 Object、Array、Date、RegExp、Function、Error、Number、String、Boolean
②内置对象——包含 Math、Global(window,在 js 中就是全局变量),应用的时候不须要 new
③宿主对象——包含自定义对象、DOM、BOM
24: 扭转 this 指向的办法
第一种:new 关键字扭转 this 指向
function Fn(){this.user = "追梦子";}
var a = new Fn();
console.log(a.user); // 追梦子
用变量 a 创立了一个 Fn 的实例(相当于复制了一份 Fn 到对象 a 外面),此时仅仅只是创立,并没有执行,而调用这个函数 Fn 的是对象 a,那么 this 指向的天然是对象 a,那么为什么对象 a 中会有 user,因为你曾经复制了一份 Fn 函数到对象 a 中,用了 new 关键字就等同于复制了一份第二种:call()
第二种:call()var a = {
user:"追梦子",
fn:function(){console.log(this.user); // 追梦子
}
}
var b = a.fn;
b.call(a); // 若不必 call,则 b()执行后 this 指的是 Window 对象
把 b 增加到第一个参数的环境中,简略来说,this 就会指向那个对象。
第三种:apply()var a = {
user:"追梦子",
fn:function(){console.log(this.user); // 追梦子
}
}
var b = a.fn;
b.apply(a);
第四种:bind()var a = {
user:"追梦子",
fn:function(){console.log(this.user);
}
}
var b = a.fn;
b.bind(a); // 代码没有被打印
咱们发现代码没有被打印,对,这就是 bind 和 call、apply 办法的不同,实际上 bind 办法返回的是一个批改过后的函数。
25:es6 新个性
1. 变量申明
let 与 const:
能够把 let 看成 var,只是它定义的变量被限定在了特定范畴内能力应用,而来到这个范畴则有效。const 则很直观,用来定义常量,即无奈被更改值的变量。for (let i=0;i<2;i++)console.log(i);// 输入: 0,1
console.log(i);// 输入:undefined, 严格模式下会报错
2. 类的反对
ES6 中增加了对类的反对,引入了 class 关键字(其实 class 在 JavaScript 中始终是保留字,目标就是思考到可能在当前的新版本中会用到,当初终于派上用场了)。JS 自身就是面向对象的,ES6 中提供的类实际上只是 JS 原型模式的包装。当初提供原生的 class 反对后,对象的创立,继承更加直观了,并且父类办法的调用,实例化,静态方法和构造函数等概念都更加形象化。// 类的定义
class Animal {
//ES6 中新型结构器
constructor(name) {this.name = name;}
// 实例办法
sayName() {console.log('My name is'+this.name);
}
}
// 类的继承
class Programmer extends Animal {constructor(name) {
// 间接调用父类结构器进行初始化
super(name);
}
program() {console.log("I'm coding...");
}
}
// 测试咱们的类
var animal=new Animal('dummy'),
wayou=new Programmer('wayou');
animal.sayName();// 输入‘My name is dummy’wayou.sayName();// 输入‘My name is wayou’wayou.program();// 输入‘I'm coding...’
3. 字符串模板
字符串模板绝对简略易懂些。ES6 中容许应用反引号 ` 来创立字符串,此种办法创立的字符串外面能够蕴含由美元符号加花括号包裹的变量 ${vraible}。如果你应用过像 C# 等后端强类型语言的话,对此性能应该不会生疏。
// 产生一个随机数
var num=Math.random();
// 将这个数字输入到 console
console.log(`your num is ${num}`);
4. 解构:主动解析数组或对象中的值。比方若一个函数要返回多个值,惯例的做法是返回一个对象,将每个值做为这个对象的属性返回。但在 ES6 中,利用解构这一个性,能够间接返回一个数组,而后数组中的值会主动被解析到对应接管该值的变量中。var [x,y]=getVal(),// 函数返回值的解构
[name,,age]=['wayou','male','secrect'];// 数组解构
function getVal() {return [ 1, 2];
}
console.log('x:'+x+', y:'+y);// 输入:x:1, y:2
console.log('name:'+name+', age:'+age);// 输入:name:wayou, age:secrect
5.Promise:
Promises 是解决异步操作的一种模式,之前在很多三方库中有实现,比方 jQuery 的 deferred 对象。当你发动一个异步申请,并绑定了.when(), .done()等事件处理程序时,其实就是在利用 promise 模式。// 创立 promise
var promise = new Promise(function(resolve, reject) {
// 进行一些异步或耗时操作
if (/* 如果胜利 */) {resolve("Stuff worked!");
} else {reject(Error("It broke"));
}
});
// 绑定处理程序
promise.then(function(result) {
//promise 胜利的话会执行这里
console.log(result); // "Stuff worked!"
}, function(err) {
//promise 失败会执行这里
console.log(err); // Error: "It broke"
});
26.promise 的了解
ES6 提供的解决异步解决办法
有两个长处
1.promise 对象的状态不受外界影响
-pending 初始状态
-fulfilled 胜利状态
-rejected 失败状态
2.promise 的状态一旦扭转,就不会再变,状态不可逆,只能由 pending 变成 pending 变成 fulfilled 或者由 pending 变成 rejected
三个毛病
1. 无奈勾销 promise,一旦新建它就会立刻执行,无奈中途勾销
2. 如果不设置回调函数,promise 外部抛出的谬误,不会反映到内部
3. 当处于 pending 状态时,无奈得悉目前停顿到哪一个阶段
用法
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作胜利 */){resolve(value);
} else {reject(error);
}
});
27. 同源策略
同源策略是浏览器的一个平安性能,不同源的客户端脚本在没有明确受权的状况下,不能读写对方资源。同源指的是协定,域名和端口号均雷同则属于同源
28. 前端跨域的形式
应用 jsonp 跨域,因为 script 标签引入的 js 是不受同源策略的限度,通过 script 标签引入一个 js 或者是一个其余后缀模式(如 php,jsp 等)的文件,此时文件返回一个 JS 函数的调用
通过 cors 跨域,实现 cors 通信的要害是服务器,只有服务器实现 cors 接口,就能够跨域
<script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://segmentfault.com/u/trigkit4/",true);
xhr.send();
</script>
反向代理跨域,反向代理指的是在前端的服务器环境中,短暂的开启一个后端服务器,由后端服务器进行数据申请,而后在将后果返回给前端
29.AMD,CMD 模块化
模块化的开发方式能够进步代码复用率,不便进行代码的治理。通常一个文件就是一个模块,有本人的作用域,只向外裸露特定的变量和函数。目前风行的 js 模块化标准有 CommonJS、AMD、CMD 以及 ES6 的模块零碎
1、AMD 推崇依赖前置,在定义模块的时候就要申明其依赖的模块
2、CMD 推崇就近依赖,只有在用到某个模块的时候再去 require
这种区别各有优劣,只是语法上的差距,而且 requireJS 和 SeaJS 都反对对方的写法
AMD 和 CMD 最大的区别是对依赖模块的执行机会解决不同,留神不是加载的机会或者形式不同
30. 报表绘图类的框架
highcharts http://www.highcharts.com/
jscharts http://www.jscharts.com/
AdminLTE http://adminlte.la998.com/
31. 库和插件的源代码
库和框架都是一种有别于软件、面向程序开发者的产品模式。
库是将代码汇合成的一个产品,供程序员调用。面向对象的代码组织模式而成的库也叫
类库。框架则是为解决一个 (一类) 问题而开发的产品,框架用户个别只须要应用框架提供的类
或函数,即可实现全副性能。
32. 函数柯里化是什么?
柯里化(英语:Currying),又称为局部求值,是把承受多个参数的函数变换成承受一个繁多参数(最后函数的第一个参数)的函数,并且返回一个新的函数的技术,新函数承受余下参数并返回运算后果。具体内容见 https://juejin.im/entry/58b316d78d6d810058678579
33. 哪些状况下会造成内存透露?
1)意外的全局变量引起的内存泄露
```javascript
function leak () {leak="xxx"; //leak 成为一个全局变量,不会被回收 相当于 window.leak = 'XXX'}
```
2. 闭包能够维持函数内局部变量,使其得不到开释。3. 没有清理的 DOM 元素援用
4. 被忘记的定时器或者回调
34. 性能优化 ?
1. 定义局部变量. 查找局部变量比全局变量要快。2. 不滥用闭包。3. 合并 js 文件, 缩小 http 申请
4. 防止应用 for-in 循环
5. 尽量不必 with,eval 语句,try-catch 的 catch 子句要审慎应用
35. 工作中闭包的应用案例?应用过什么闭包工具库嘛
1. 闭包经典应用场景一:通过循环给页面上多个 dom 节点绑定事件
```javascript
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<button>Button0</button>
<button>Button1</button>
<button>Button2</button>
<button>Button3</button>
<button>Button4</button>
</body>
</html>
for(var i = 0, len = btns.length; i < len; i++) {(function(i) {btns[i].onclick = function() {alert(i);
}
}(i))
}
```
2. 封装变量 闭包能够将一些不心愿裸露在全局的变量封装成“公有变量”。3. 闭包应用场景三:连续局部变量的寿命
闭包工具库:???
36.301 302 如何重定向 ?
301 redirect: 301 代表永久性转移(Permanently Moved)
302 redirect: 302 代表暂时性转移(Temporarily Moved)
具体来说,301 和 302 状态码都示意重定向,就是说浏览器在拿到服务器返回的这个状态码后会主动跳转到一个新的 URL 地址,这个地址能够从响应的 Location 首部中获取(用户看到的成果就是他输出的地址 A 霎时变成了另一个地址 B)——这是它们的共同点。他们的不同在于。301 示意旧地址 A 的资源曾经被永恒地移除(这个资源不可拜访了),搜索引擎在抓取新内容的同时也将旧的网址替换为重定向之后的网址;302 示意旧地址 A 的资源还在(依然能够拜访),这个重定向只是长期地从旧地址 A 跳转到地址 B,搜索引擎会抓取新的内容而保留旧的网址。
37. 暂时性死区?
举例:```javascript
console.log (a) // 因为变量晋升,输入 undefined
var a
```
```javascript
console.log(a) // 报错 ReferenceError: a is not defined
let a
```
ES6 规定,let/const 命令会使区块造成关闭的作用域。若在申明之前应用变量,就会报错。总之,在代码块内,应用 let 命令申明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
38. 堆和栈
- 栈(stack):为主动调配的内存空间,他由零碎主动开释。寄存在栈内存中的简略数据段,数据大小确定,内存空间大小能够调配,是间接按值寄存的,所以能够间接拜访。根本的数据类型放在栈中
- 堆(heap):则是动态分配的内存,大小不定也不会主动开释。援用类型(object)是寄存在堆内存中的,变量实际上是一个寄存在栈内存的指针,这个指针指向堆内存中的地址。每个空间大小不一样,要依据状况进行特定的调配。
39. 箭头函数的 this 指向
- 箭头函数默认不会应用本人的 this,而是会和外层的 this 保持一致,最外层的 this 就是 window 对象。在多层对像嵌套里箭头函数里 this 是和最最外层保持一致的
40. 深拷贝和浅拷贝的区别
- 浅拷贝:只复制指向某个对象的指针,而不复制对象自身,新旧对象共享一块内存
- 深拷贝:复制并创立一个截然不同的对象,不共享内存,批改新对象,旧对象放弃不变。
41. 手写一个 promise,采纳 es5 的方面。promise 的源码了解
-
var fn=function(resolve, reject){console.log('begin to execute!'); var number=Math.random(); if(number<=0.5){resolve('less than 0.5'); }else{reject('greater than 0.5'); } } var p=new Promise(fn); p.then(function(data){console.log('resolve:', data); }, function(data){console.log('reject:', data); }) 对 promise 源码的了解:当咱们运行 var p=new Promise(fn) 这条语句的时候,fn 函数就曾经执行了,然而,p.then 这个办法是在前面才定义了 resolve 和 reject,那么为何 fn 函数可能晓得 resolve 和 reject 函数是什么呢?换句话说,resolve 和 reject 函数是如何回到过来,呈现在先执行的 fn 函数当中的呢?要解决这个问题,次要使用的就是 setTimeout 这个办法,来提早 fn 当中 resolve 和 reject 的执行。咱们晓得 js 是单线程 + 音讯队列,必须等主线程代码执行结束能力开始执行音讯队列当中的代码。因而,会首先执行 then 这个办法,给外面两个参数赋值。退出状态:pending, resolved, rejected 在 Promise 标准当中,规定 Promise 只能从初始 pending 状态变到 resolved 或者 rejected 状态,是单向变动的,也就是说执行了 resolve 就不会再执行 reject,反之亦然。并在必要的中央进行判断,避免反复执行。function MyPromise(fn) { this.value; this.status = 'pending'; this.resolveFunc = function() {}; this.rejectFunc = function() {}; fn(this.resolve.bind(this), this.reject.bind(this)); } MyPromise.prototype.resolve = function(val) { var self = this; if (this.status == 'pending') { // 判断状态 this.status = 'resolved'; this.value=val; setTimeout(function() {self.resolveFunc(self.value); }, 0); } } MyPromise.prototype.reject = function(val) { // 判断状态 var self = this; if (this.status == 'pending') { this.status = 'rejected'; this.value=val; setTimeout(function() {self.rejectFunc(self.value); }, 0); } } MyPromise.prototype.then = function(resolveFunc, rejectFunc) { this.resolveFunc = resolveFunc; this.rejectFunc = rejectFunc; } 链式调用:要实现链式调用,then 办法的返回值也必须是一个 Promise 对象,这样能力再次在前面调用 then。
## 42.async 与 promise 的区别:- 在函数前有一个关键字 `async`,`await` 关键字只能在应用 `async` 定义的函数中应用。任何一个 `async` 函数都会隐式返回一个 `promise`,并且 promise resolve 的值就是 return 返回的值
- 不能在函数结尾应用 `await`
- Async 函数的实现最简洁,最合乎语义,简直没有语义不相干的代码。- Promise 的写法比回调函数的写法大大改良,然而一眼看上去,代码齐全都是 Promise 的 API(`then`、`catch` 等等),操作自身的语义反而不容易看进去。## 43.async 是 promise 裸露进去的语法糖?- async 函数就是 generator 函数的语法糖
## 44. 箭头函数与 function 的区别?**(1) 箭头函数与 function 定义函数的写法:**
//function
function fn(a, b){return a + b;}
//arrow function
var foo = (a, b)=>{return a + b};
**(2) this 的指向:**
应用 function 定义的函数,this 的指向随着调用环境的变动而变动的,而箭头函数中的 this 指向是固定不变的,始终指向的是定义函数的环境。**(3) 构造函数 **
function 是能够定义构造函数的,而箭头函数是不行的。**(4) 变量晋升 **
因为 js 的内存机制,function 的级别最高,而用箭头函数定义函数的时候,须要 var(let const 定义的时候更不用说)关键词,而 var 所定义的变量不能失去变量晋升,故箭头函数肯定要定义于调用之前!## 45. 箭头函数中没有 this 对象,this 是最近的 this
(1) 因为箭头函数不绑定 this,它会捕捉其所在(即定义的地位)上下文的 this 值,作为本人的 this 值
(2)办法的箭头函数 this 指向全局 window 对象,而一般函数则指向调用它的对象, 箭头函数没有 this
## 46. 箭头函数能够作为构造函数吗?因为箭头函数没有本人的 this,它的 this 其实是继承了外层执行环境中的 this,且 this 指向永远不会随在哪里调用、被谁调用而扭转,所以箭头函数不能作为构造函数应用,或者说构造函数不能定义成箭头函数,否则用 new 调用时会报错!## 47. 箭头函数代替 arguments 的办法?箭头函数没有本人的 arguments 对象。在箭头函数中拜访 arguments 实际上取得的是外层部分(函数)执行环境中的值。能够在箭头函数中应用 rest 参数代替 arguments 对象,来拜访箭头函数的参数列表
## 48. 解构和负载的场景?1. ** 解构赋值 **,即对某种构造进行解析,而后将解析进去的值赋值给相干的变量,常见的有数组、对象、字符串的解构赋值等
- 数组解构
只有等号两边的模式雷同,右边的变量就会被赋予对应的值。
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
```
-
对象构造
对象的解构与数组有一个重要的不同。数组的元素是按秩序排列的,变量的取值由它的地位决定;而对象的属性没有秩序,变量必须与属性同名,能力取到正确的值。
let {bar, foo} = {foo: 'aaa', bar: 'bbb'}; foo // "aaa" bar // "bbb"
-
字符串构造
字符串被转换成了一个相似数组的对象
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
- 负载
49. 扩大运算符理解?
-
概念
扩大运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。扩大运算符与失常的函数参数能够联合应用,前面也能够搁置表达式,但如果前面是一个空数组,则不产生任何成果。
let arr = []; arr.push(...[1,2,3,4,5]); console.log(arr); //[1,2,3,4,5] console.log(1, ...[2, 3, 4], 5) //1 2 3 4 5 console.log(...(1 > 0 ? ['a'] : [])); //a console.log([...[], 1]); //[1]
-
利用
1 代替函数的 apply 办法
因为扩大运算符能够开展数组,所以不再须要 apply 办法,将数组转为函数的参数了。// ES5 的写法 Math.max.apply(null, [14, 3, 77]) // ES6 的写法 Math.max(...[14, 3, 77])
2 复制数组
// ES5 的写法 const a1 = [1, 2]; const a2 = a1.concat(); // ES6 的写法 const a1 = [1, 2]; onst a2 = [...a1]; // 或 const [...a2] = a1;
3 合并数组
// ES5 的写法 [1, 2].concat(more); arr1.concat(arr2, arr3); // ES6 的写法 [1, 2, ...more]; [...arr1, ...arr2, ...arr3]
4 与构造赋值联合
// ES5 的写法 a = list[0], rest = list.slice(1) // ES6 的写法 [a, ...rest] = list
50. 深拷贝的办法?
答:对数组进行深拷贝:
- for 循环
- slice 办法
- concat 办法
- ES6 扩大运算符
对对象进行深拷贝: - for 循环
- 先转换成 json 在转换成对象
- ES6 扩大运算符
51. 扩大运算符的办法进行拷贝,是深拷贝还是浅拷贝?
答:深拷贝
52.set 的用法:数组去重。
答:有两种办法:
办法一:Set + Array.from()
var set1 = Array.from(new Set([1,1,2,2,33,'33',44,'44']))
办法二:…[拓展运算符] + Set
var tt = [...new Set([5,5,6,6,8,])] // 5,6,8
53.es6 判断是否是数组:isArray,以及其余判断数组的办法?typeof 检测数组返回值
答:
判断数组办法:
-
1. 用 instanceof 判断
应用 instanceof 运算符能够分辨数组和对象,能够判断数组是数组。
-
2. 用 constructor 判断
实例化的数组领有一个 constructor 属性,这个属性指向生成这个数组的办法。
当 constructor 属性被批改之后,就无奈用这个办法判断数组是数组了 -
3. 用 Object 的 toString 办法判断
toString 办法将会返回 ”[object type]”,其中的 type 代表的是对象的类型,依据 type 的值,咱们就能够判断这个疑似数组的对象到底是不是数组了
- 4. 用 Array 对象的 isArray 办法判断
isArray 办法返回 true,当参数不为数组的时候,isArray 办法返回 false
typeof 检测数组返回值
typeof 是 javascript 原生提供的判断数据类型的运算符,它会返回一个示意参数的数据类型的字符串
54. 数组去重的办法?
答:
- 利用 ES6 中的 Set
- 利用 for 嵌套 for,而后 splice 去重(ES5 中最罕用)
- 利用 indexOf 去重
- 利用 sort()
- 利用 includes
- 利用 hasOwnProperty
- 利用 filter
- 利用递归去重
- 利用 Map 数据结构去重
- 利用 reduce+includes
55. 数组中呈现反复数字的反复统计?
思路:
// 统计一个数组中有多少个不反复的单词:// 不必 reduce 时:var arr = ["apple","orange","apple","orange","pear","orange"];
function getWordCnt(){var obj = {};
for(var i= 0, l = arr.length; i< l; i++){var item = arr[i];
obj[item] = (obj[item] +1 ) || 1;
}
return obj;
}
console.log(getWordCnt());//{apple: 2, orange: 3, pear: 1}
// 用 reduce 时:var arr = ["apple","orange","apple","orange","pear","orange"];
function getWordCnt(){return arr.reduce(function(prev,next){prev[next] = (prev[next] + 1) || 1;
return prev;
},{});
}
console.log(getWordCnt());//{apple: 2, orange: 3, pear: 1}
56. 数组常见的 api
- 数组 api 又叫利用程序接口,函数办法
-
常见 api
- arr.push(): 数据增加 – 在数组尾部增加元素
push 办法一次可增加单个或多个元素到数组末端,也能够增加数组数组名.push("元素 1","元素 2",...)
- arr.pop(): 数据更新 – 删除数组的最初一个元素
pop 办法的作用是移除数组开端的一个元素。把数组长度减 1,并且返回它的删除值,如果数组为空,则 pop() 不扭转数组,返回 undefind数组名.pop()
- arr.concat(): 连贯两个或更多的数组,并返回后果。
- arr.join(): 字符连贯
若不想要任何连接符,则括号中用空字符即可。数组名.join("连接符")
- arr.reverse(): 颠倒数组中元素的程序。
- arr.shift(): 删除数据 – 移除数组顶端的元素
shift 办法与 pop 相同,移除数组的第一个元素并将其返回。该办法执行后,数组剩下的元素向前挪动,下标索引号从新调整从 0 开始。数组名.shift()
- arr.unshift(): 增加数据 – 在数组头部增加元素
nshift 办法与 push 办法正好相同,是将元素插入数组的首部。一次能够插入单个或多个元素,所有元素按程序插入,操作实现后返回新数组的援用数组名.unshift("元素 1","元素 2",...)
- arr.slice(): 生成特定数据 – 获取数组中的一部分元素
slice 办法的作用是抽取数组的一段元素,抽取指定下标索引区间中的元素作为新数组返回数组名.slice(start,end)
注:splice 是间接批改原数组,而 slice 不会批改原数组。 - arr.sort():对数组的元素进行排序
- arr.splice(): 更新挪动数据 – 删除、替换或插入数组元素
splice 办法的作用是,从一个数组中移除一个或多个元素。剩下的元素组成一个数组,移除的元素组成另一个数组并返回它的援用。同时,原数组能够在移除的开始地位处顺带插入一个或多个新元素,达到批改替换数组元素的目标。这个操作成果通常称为接合数组名.splice(start,deleteCount,item1,item2,...)
参数阐明:
start:必选项,示意从数组中剪切的起始地位下标索引号。
deteleCount:必选项,示意从数组中切取的元素个数。
item:可选项,示意切取时插入原数组切入点开始出的一个或多个元素。 - arr.indexof(): 查找元素对应下标
- arr.forEach(): 数组遍历
- arr.filter(): 数组遍历,将返回的值新建成一个新数组
- arr.map(): 遍历数组,间接操作数组,返回一个新数组
- arr.reduce(): 数组归并
- arr.push(): 数据增加 – 在数组尾部增加元素
57. 数组中 sort 的用法。sort 的返回值?没有传比拟函数的比拟?
用法:对数组的元素进行排序
arr.sort() 间接操作原有数组,返回原有数组,当没有传比拟函数的话不会按大小排序,而是按程序排序
var arr = [22,12,3,43,56,47,4];
arr.sort();
console.log(arr); // [12, 22, 3, 4, 43, 47, 56]
arr.sort(function (m, n) {if (m < n) return -1
else if (m > n) return 1
else return 0
});
console.log(arr); // [3, 4, 12, 22, 43, 47, 56]
58.canvas 与 webGL 的区别
- 概要
Canvas 位图,是须要本人画点的白板;WebGL 3D 位图,是基于 Canvas 的 3D 框架。- 用处
Canvas 实用于位图,高数据量高绘制频率(帧率)的场景,如动画、游戏;WebGL 次要用来做 3D 展现、动画、游戏。- canvas 毛病:- 只能绘制 2D 图像,临时不反对 3D 图像。-canvas 绘制图形出并非能够间接操作的 dom 对象。如果要对其进行相似 dom 的操作,例如增加属性等等,比拟麻烦(这就是为什么必须应用类库)。- canvas 长处:- 因为 canvas 绘图不会给每个点生成对象,所以绘制速度快,耗费内存少。(这点次要是绝对于 SVG,VML 技术而言)- 兼容性较好。除了 IE6,其余浏览器都能够反对。(IE7,8 须要载入扩大 JS,究竟还是能用的)- webGL
是一项应用 JavaScript 实现 3D 绘图的技术,浏览器无需插件反对,Web 开发者间接应用 js 调用相干 API 就能借助零碎显卡(GPU)进行编写代码从而出现 3D 场景和对象。
59.BOM 对象与 DOM 对象的区别?实现由 BOM 对象还是先有 DOM 对象
`
BOM:浏览器对象模型(Brower Object Model),是用于操作浏览器而呈现的 API,BOM 对象则是 Javascript 对 BOM 接口的实现。BOM 提供了独立于内容的、能够与浏览器窗口进行交互的对象构造。通过 BOM 对象能够拜访浏览器性能部件和属性。BOM 中代表浏览器窗口的 window 对象是 Javascript 顶层对象,其余 BOM 对象均为 window 对象的子对象。被作为 window 对象的属性来援用。其余 BOM 对象都是在 window 对象中进行操作。DOM 是文档对象模型,比方 html 是树结构的,操作 dom 就是操作这颗树:DOM:文档对象模型(Document Object Model),是 W3C 定义的一套用于解决 HTML 和 XML 文档内容的规范编程接口 API。javascript 实现 DOM 接口的对象对应的是 document 对象,JS 通过该对象来对 HTML/XML 文档进行增删改查。DOM 定义了 HTML 和 XML 的逻辑构造,将整个页面划分成由档次节点形成的文档,以树的模式来展示,如下面那张图所示。在 BOM 和 DOM 构造档次图中,document 对象属于 window 对象,所以 DOM 也能够看作是 BOM 的一部分
`