· 同步和异步的区别?
同步:指的是主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
异步:不进入主线程,而进入‘任务队列’的任务,只有等主线程任务执行完毕,‘任务队列’开始通知主线程,请求执行任务,该任务才会进入主线程执行。
异步实现的几种方式:
- 回调函数callback。比如:发起ajax请求后,会回调success、error函数,对数据进行处理。发起请求后进程还是会正常运行,不必等到请求成功后才会执行后面的进程。
- 事件驱动。为页面默认元素绑定事件。
- promise模式。通常实现一种then方法,用来注册状态发生改变时作为对应的回调函数。
· 如何解决跨域问题?
1. 在发起请求时,给请求头部(header)添加Access-Control-Allow-Origin属性,属性值是"*",如("Access-Control-Allow-Origin", "*"。
2. JSONP方式。如下:
$.ajax({
url: url,
type: 'get',
dataType: 'jsonp', // 数据类型为jsonp
jsonp: 'backFunction', // 服务器端用于接收callBack调用的函数名的参数
success: function () {},
error: function () {}
})
3. nginx反向代理。
· 页面编码和被请求的资源编码如果不一致如何处理?
对于ajax请求传递的参数,如果是get请求,在有些浏览器会乱码。因为不同浏览器对参数编码的处理方式不一样。所以对于get请求的参数需要使用encodeURIComponent函数对参数进行编码处理。对于post请求则不需要进行编码。
· 模块化开发怎么做?
模块化:理论上是一个功能对应一个模块,互不影响,在需要的时候载入。
比如Vue.js里的模块/组件:就是将一段js、html、css组合在一起,形成有一定功能的代码片段。
· AMD(Modules/Asynchronous-Definition)、CMD(Common Module Definition)规范区别?
1. AMD是RequireJS在推广过程中对模块定义的规范化产出;CMD是SeaJS在推广过程中对模块地定义的规范化产出。
2. 对于依赖的模块,AMD是提前运行,CMD是延迟运行。不过RequireJS在2.0开始,也可以使用延迟加载。
· 谈一谈你对ECMAScript6的了解?
是JavaScript语言的下一代标准。ECMAScript规定了JavaScript的规格。它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
· ECMAScript6 怎么写class,为什么会出现class这种东西?
/** ECMAScript6使用class关键词:如下 */
class Study {
constructor(x, y) {
this.x = x
this.y = y
}
fun () {
console.log(this.x)
}
}
/** class关键词的出现:由于生成实例的传统方法是通过构造函数,这种写法与传统的面向对象差异大,学习起来会比较困难。而ES6引入class(类),class可以看作只是一个语法糖,通过class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。 */
· 异步加载的方式有哪些?
1. 通过创建DOM来加载script,如下:
(function () {
var scrptDom = document.createElement('script')
scriptDom.type = 'text/javascript'
script.async = true
script.src = 'http://www.url.com'
var x = document.getElementsByTagName('head')[0]
x.insertBefore(script, x.firstChild)
})()
/** 不过这种加载方式执行完之前还是会阻止onload事件的触发,而现在也有很多页面的代码都在onload时还执行额外的渲染工作,所以还是会阻塞部分页面的初始化处理 */
2. onload,如下:
(function () {
if (window.attachEvent) {
window.attachEvent('load', asyncload)
} else {
window.addEventListener('load', asyncload)
}
var asyncload = function () {
var scrptDom = document.createElement('script')
scriptDom.type = 'text/javascript'
script.async = true
script.src = 'http://www.url.com'
var x = document.getElementsByTagName('head')[0]
x.insertBefore(script, x.firstChild)
}
})()
/** 这种方法把插入script的方法放到一个函数里,然后放到onload函数中执行,这样就解决了onload阻塞的问题。(reload:是dom加载完成后执行,onload:是所有内容(包括图片、css、js等文件)都加载完成后执行) */
3. jQuery的$(document).ready(function() {})相当于$(function (){}),如下:
$(document).ready(function () {
console.log(11)
})
/** 或 */
$(function () {
console.log(11)
})
4. script标签的async='async'属性,如下:
/**
<script type="text/javascript" src="xxx.js" async="async"></script>
注意:1. 此方法不能保证脚本按顺序执行。 2. 它们将在onload事件之前完成
*/
5. script标签的defer='defer'属性,如下:
/**
<script type="text/javascript" src="xxx.js" defer="defer"></script>
注意:1. 此方法保证脚本按顺序执行。
2. 它们将在onreload事件完成后。
*/
· documen.write 和 innerHTML的区别?
documen.write是直接写入页面的内容流。每次写完关闭之后再重新调用该函数,会导致重绘整个页面。
documen.innerHTML是写入某个DOM节点的内容,重新调用该函数,是不会导致重绘整个页面。实现局部刷新的效果。
· DOM操作——怎样添加、移除、替换、插入、复制和查找节点?
// dom节点的添加
var addDom = document.createElement('div')
addDom.setAttribute('class', 'add-dom')
addDom.innerHTML = 'DOM节点'
document.getElementsByTagName('body')[0].append(addDom)
// DOM节点移除
var removeDom = document.getElementById('remove-dom')
removeDom.addEventListener('click', function () {
removeDom.remove()
})
// 替换DOM
var moveDom = document.getElementById('move-dom')
moveDom.addEventListener('click', function () {
var newDom = document.getElementsByClassName('replace-dom')[0]
console.log(document.getElementsByTagName('body')[0])
document.getElementsByTagName('body')[0].replaceChild(newDom, moveDom)
})
// 插入节点
var insertDom = document.getElementById('insert-dom')
insertDom.addEventListener('click', function () {
var newDom = document.createElement('div')
newDom.innerHTML = '插入的新节点'
document.getElementsByTagName('body')[0].insertBefore(newDom, insertDom.nextSibling) // 插在后面
document.getElementsByTagName('body')[0].insertBefore(newDom, insertDom) // 插在前面
})
// 复制节点
var copyDom = document.getElementsByClassName('copy-dom')[0]
console.log(copyDom)
copyClick(copyDom)
function copyClick (dataDom) {
dataDom.addEventListener('click', function () {
var newDom = dataDom.cloneNode(true)
var doom = document.getElementsByTagName('body')[0].insertBefore(newDom, dataDom.nextSibling)
console.log(doom)
copyClick(doom)
})
}
// 查找节点
var seek = document.getElementById('seek-dom')
seek.addEventListener('click', function () {
console.log(seek.children) // 包含节点的数组
console.log(seek.childNodes) // 会计算空节点(换行)
console.log(seek.firstChild) // 返回第一个节点,空节点(换行)
console.log(seek.firstElementChild) // 返回第一个实节点
console.log(seek.lastChild) // 返回最后一个节点,空节点(换行)
console.log(seek.lastElementChild) // 返回最后一个实节点
console.log(seek.nextSibling) // 某个节点的下一个节点(换行)
console.log(seek.nextElementSibling) // 某个节点的下一个实体节点
console.log(seek.previousSibling) // 某个节点的上一个节点(换行)
console.log(seek.previousElementSibling) // 某个节点的上一个实节点
console.log(seek.parentNode) // 某个节点的上一级父节点
console.log(seek.offsetParent) // 返回第一个有定位属性的父节点,如果没有,直接返回body
})
· .call() 和 .apply() 的含义和区别?
call:调用某个对象的方法,用另一个对象替换调用的这个对象。如下:
A.call(B, arg1, arg2)
/** 上述的作用是:B对象应用A对象的方法 */
apply:调用某个对象的方法,用另一个对象替换调用的这个对象。如下:
A.apply(B, [arg1, arg2])
/** 上述的作用是:B对象应用A对象的方法 */
区别:
apply:只有两个参数,第一个是this对象,第二个参数是给该方法传递的参数并且只能是数组。
call:可以接受多个参数,传给该方法的参数只能一一列出来。
/** apply */
A.apply(B, [1, 2, 3, 4, 5])
/** call */
A.call(B, 1, 2, 3, 4, 5)
· 数组和对象有哪些原生方法,列举一下?
1. 赋值方法:
- pop和push:数组从最后添加删除元素。
- shift和unshift:数组从前面添加删除元素。
- splice:从数组中删除添加元素。
- reverse:倒序。
- sort:排序。
2. 访问方法:
- concat:用于连接两个或多个数组(返回一个新数组)。
- join:用于把数组中所有元素放入字符串,然后通过制定字符隔开。
- slice:根据制定的字符把字符串转成数组(返回一个新数组)。
- toStrong:用于把一个逻辑值转成字符串并返回结果。
- indexOf:可返回某个指定的字符串值在字符串中首次出现的位置。
- lastIndexOf:可返回某个指定的字符串值在字符串中最后出现的位置。
3. 迭代方法:
- forEach:循环数组的每个元素并把元素传递给回调函数,返回三个参数,依次是元素、索引、数组本身。
- map:使得数组的每个元素都会调用一个提供的函数,经过处理后返回其结果。
- filter:创建一个新数组,新数组中的元素通过检查指定数组中符合条件的所有元素。
- some:依次检测数组中的元素是否满足指定条件,如果有一个元素满足就返回true,如果没有元素满足就会返回false。
- every:依次检测数组中的元素是否满足指定条件,如果有一个元素满足就返回false,如果没有元素满足就会返回true。
- reduce:接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
- reduceRight:接收一个函数作为累加器,数组中的每个值(从数组末尾向前)开始缩减,最终计算为一个值。
· JS 怎么实现一个类。怎么实例化这个类?如下
/** 通过class创建一个类 */
class Pant {
constructor (name) {
this.name = name
}
pantFun () {
console.log(this.name)
}
}
/** 通过new实例化这个类 */
var obj = new Pant('hello')
· 那些操作会造成内存泄漏?
内存泄漏:就是不再需要的对象仍然存在内存中,内存泄漏不断堆积的后果就是内存溢出,就是内存不够用。
造成泄漏的操作:
- setTimeout的第一个参数使用字符串而非函数的话,会造成内存泄漏。
- 全部变量。
- 闭包。
- 循环。
- dom清空或删除时,事件未清除导致内存泄漏。