一、js的基本类型有哪些?引用类型有哪些?null和undefined的区别?
1、基本类型
Number,String,Undefined,Null,Boolean
2、引用类型
Object,Function
3、null和undefined的区别
Undefined类型只有一个值,即undefined。当声明的变量还未被初始化时,变量的默认值为undefined。
Null类型也只有一个值,即null。null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。
二、如何判断一个变量是Array类型?如何判断一个变量是Number类型?(都不止一种)
1、方法一: instanceof
instanceof 用于判断一个变量是否某个对象的实例,左边操作数是一个对象,右边操作数是一个函数对象或者函数构造器。原理是通过判断左操作数的对象的原型链上是否具有右操作数的构造函数的prototype属性
var arr=[];
arr instanceof Array
2、constructor
constructor 属性返回对创建此对象的数组函数的引用,就是返回对象相对应的构造函数。
[].constructor === Array
3、 Object.prototype.toString.call(arr) === “[object Array]”
Object.prototype.toString.call(o) === ‘[object Array]‘;
二、JS常见的dom操作api
1、Node
Node是一个接口,中文叫节点,很多类型的DOM元素都是继承于它,都共享着相同的基本属性和方法。常见的Node有 element,text,attribute,comment,document 等(所以要注意 节点 和 元素 的区别,元素属于节点的一种)。
Node有一个属性 nodeType 表示Node的类型,它是一个整数,其数值分别表示相应的Node类型
ELEMENT_NODE: 1, // 元素节点
ATTRIBUTE_NODE: 2, // 属性节点
TEXT_NODE: 3, // 文本节点
COMMENT_NODE: 8, // 注释节点
DOCUMENT_NODE: 9, // 文档
DOCUMENT_FRAGMENT_NODE: 11, // 文档碎片
2、NodeList
NodeList 对象是一个节点的集合,一般由 Node.childNodes 、 document.getElementsByName 和 document.querySelectorAll 返回的。
不过需要注意, Node.childNodes 、 document.getElementsByName 返回的 NodeList 的结果是实时的(此时跟HTMLCollection比较类似),而 document.querySelectorAll 返回的结果是固定的
3、HTMLCollection
HTMLCollection是一个特殊的NodeList,表示包含了若干元素(元素顺序为文档流中的顺序)的通用集合,它是实时更新的,当其所包含的元素发生改变时,它会自动更新。另外,它是一个伪数组,如果想像数组一样操作它们需要像 Array.prototype.slice.call(nodeList, 2) 这样调用。
4、节点查找API
document.getElementById :
根据ID查找元素,大小写敏感,如果有多个结果,只返回第一个;
document.getElementsByClassName :
根据类名查找元素,多个类名用空格分隔,返回一个 HTMLCollection 。注意兼容性为IE9+(含)。另外,不仅仅是document,其它元素也支持 getElementsByClassName 方法;
document.getElementsByTagName :
根据标签查找元素, * 表示查询所有标签,返回一个 HTMLCollection 。
document.getElementsByName :
根据元素的name属性查找,返回一个 NodeList 。
document.querySelector :
返回单个Node,IE8+(含),如果匹配到多个结果,只返回第一个。
document.querySelectorAll :
返回一个 NodeList ,IE8+(含)。
document.forms :
获取当前页面所有form,返回一个 HTMLCollection ;
5、节点创建API
createElement
通过 createElement 创建的元素并不属于 document 对象,它只是创建出来,并未添加到html文档中,要调用 appendChild 或 insertBefore 等方法将其添加到HTML文档中。
createTextNode
创建文本节点
cloneNode
克隆一个节点: node.cloneNode(true/false) ,它接收一个bool参数,用来表示是否复制子元素
克隆节点并不会克隆事件,除非事件是用 <div onclick="test()"></div> 这种方式绑定的,用 addEventListener 和 node.onclick=xxx; 方式绑定的都不会复制。
createDocumentFragment
本方法用来创建一个 DocumentFragment ,也就是文档碎片,它表示一种轻量级的文档,主要是用来存储临时节点,大量操作DOM时用它可以大大提升性能。
6、节点修改AP
appendChild
parent.appendChild(child);
它会将child追加到parent的子节点的最后面。另外,如果被添加的节点是一个页面中存在的节点,则执行后这个节点将会添加到新的位置,其原本所在的位置将移除该节点,也就是说不会同时存在两个该节点在页面上,且其事件会保留。
insertBefore
parentNode.insertBefore(newNode, refNode);
将某个节点插入到另外一个节点的前面
关于第二个参数:
refNode是必传的,如果不传该参数会报错;
如果refNode是undefined或null,则insertBefore会将节点添加到末尾;
removeChild
var deletedChild = parent.removeChild(node)
removeChild用于删除指定的子节点并返回子节点
deletedChild指向被删除节点的引用,它仍然存在于内存中,可以对其进行下一步操作。另外,如果被删除的节点不是其子节点,则将会报错
replaceChild
parent.replaceChild(newChild, oldChild);
replaceChild用于将一个节点替换另一个节点
7、节点关系API
父关系API
parentNode :
每个节点都有一个parentNode属性,它表示元素的父节点。Element的父节点可能是Element,Document或DocumentFragment;
parentElement :
返回元素的父元素节点,与parentNode的区别在于,其父节点必须是一个Element元素,如果不是,则返回null;
子关系API
children :
返回一个实时的 HTMLCollection ,子节点都是Element,IE9以下浏览器不支持;
childNodes :
返回一个实时的 NodeList ,表示元素的子节点列表,注意子节点可能包含文本节点、注释节点等;
firstChild :
返回第一个子节点,不存在返回null,与之相对应的还有一个 firstElementChild ;
lastChild :
返回最后一个子节点,不存在返回null,与之相对应的还有一个 lastElementChild ;
兄弟关系型API
previousSibling :
节点的前一个节点,如果不存在则返回null。注意有可能拿到的节点是文本节点或注释节点,与预期的不符,要进行处理一下。
nextSibling :
节点的后一个节点,如果不存在则返回null。注意有可能拿到的节点是文本节点,与预期的不符,要进行处理一下。
previousElementSibling :
返回前一个元素节点,前一个节点必须是Element,注意IE9以下浏览器不支持。
nextElementSibling :
返回后一个元素节点,后一个节点必须是Element,注意IE9以下浏览器不支持。
8、元素属性型API
setAttribute
给元素设置属性:
element.setAttribute(name, value);
其中name是特性名,value是特性值。如果元素不包含该特性,则会创建该特性并赋值。
getAttribute
getAttribute返回指定的特性名相应的特性值,如果不存在,则返回null:
var value = element.getAttribute("id");
9、样式相关API
直接修改元素的样式
elem.style.color = 'red';
elem.style.setProperty('font-size', '16px');
elem.style.removeProperty('color');
动态添加样式规则
var style = document.createElement('style');
style.innerHTML = 'body{color:red} #top:hover{background-color: red;color: white;}';
document.head.appendChild(style);
window.getComputedStyle
通过 element.sytle.xxx 只能获取到内联样式,借助 window.getComputedStyle 可以获取应用到元素上的所有样式,IE8或更低版本不支持此方法。
var style = window.getComputedStyle(element[, pseudoElt]);
getBoundingClientRect
getBoundingClientRect 用来返回元素的大小以及相对于浏览器可视窗口的位置,用法如下:
var clientRect = element.getBoundingClientRect();
clientRect是一个 DOMRect 对象,包含width、height、left、top、right、bottom,它是相对于窗口顶部而不是文档顶部,滚动页面时它们的值是会发生变化的。
三、解释一下事件冒泡和事件捕获
事件捕获指的是从document到触发事件的那个节点,即自上而下的去触发事件。
相反的,事件冒泡是自下而上的去触发事件。
绑定事件方法的第三个参数,就是控制事件触发顺序是否为事件捕获。
true,事件捕获;false,事件冒泡。默认false,即事件冒泡。
四、事件委托(手写例子),事件冒泡和捕获,如何阻止冒泡?如何组织默认事件?
<ul id="ul1">
<li></li><li></li><li></li>
</ul>
window.onload = function(){
var oUl = document.getElementById("ul1");
oUl.onclick = function(){
alert(123);
}
}
用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发
2、阻止冒泡和默认行为
2.1、event.stopPropagation()方法
这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开,
2.2、event.preventDefault()方法
这是阻止默认事件的方法,调用此方法是,连接不会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素;
2.3、return false
这个方法比较暴力,他会阻止事件冒泡也会阻止默认事件;写上此代码,连接不会被打开,事件也不会传递到上一层的父元素;可以理解为return false就等于同时调用了event.stopPropagation()和event.preventDefault()
五、对闭包的理解?什么时候构成闭包?闭包的实现方法?闭包的优缺点?
function f1(){
var n=999;
var f2 = function f2(m){
return n + m;
}
return f2;
}
var fn = f1()
console.log(fn(1)); // 1000
1、由于在javascript中,只有函数内部的子函数才能读取局部变量,所以说,闭包可以简单理解成“定义在一个函数内部的函数“。
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会在f1调用后被自动清除。
2、为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
六、this有哪些使用场景?跟C,Java中的this有什么区别?如何改变this的值?
1、this 指向调用该方法的对象
有五种情况
- 情况一:全局 & 调用普通函数
在全局环境中,this 永远指向 window。 - 情况二:构造函数
如果函数作为构造函数使用,那么其中的 this 就代表它即将 new 出来的对象 - 情况三:对象方法
如果函数作为对象的方法时,方法中的 this 指向该对象
注意:若是在对象方法中定义函数,那么this 指向 window - 情况四:构造函数 prototype 属性
function Foo(){
this.x = 10;
}
Foo.prototype.getX = function () {
console.log(this); //Foo {x: 10, getX: function}
console.log(this.x); //10
}
var foo = new Foo();
foo.getX();
在 Foo.prototype.getX 函数中,this 指向的 foo 对象。不仅仅如此,即便是在整个原型链中,this 代表的也是当前对象的值。
- 情况五:函数用 call、apply或者 bind 调用。
var obj = {
x: 10
}
function foo(){
console.log(this); //{x: 10}
console.log(this.x); //10
}
foo.call(obj);
foo.apply(obj);
foo.bind(obj)();
当一个函数被 call、apply 或者 bind 调用时,this 的值就取传入的对象的值。
- 情况六:DOM event this
在一个 HTML DOM 事件处理程序里,this 始终指向这个处理程序所绑定的 HTML DOM 节点
function Listener(){
document.getElementById('foo').addEventListener('click', this.handleClick);
//这里的 this 指向 Listener 这个对象。不是强调的是这里的 this
}
Listener.prototype.handleClick = function (event) {
console.log(this); //<div id="foo"></div>
}
var listener = new Listener();
document.getElementById('foo').click();
箭头函数内部的 this 是词法作用域,由上下文确定
var obj = {
x: 10,
foo: function() {
var fn = () => {
return () => {
return () => {
console.log(this); //Object {x: 10}
console.log(this.x); //10
}
}
}
fn()()();
}
}
obj.foo();
this 总是指向词法作用域,也就是外层调用者 obj。
就不再需要了
var obj = {
x: 10,
foo: function() {
var fn = () => {
return () => {
return () => {
console.log(this); // Object {x: 10}
console.log(this.x); //10
}
}
}
fn.bind({x: 14})()()();
fn.call({x: 14})()();
}
}
obj.foo();
七、call,apply,bind
1、bing 返回的是一个新的函数,你必须调用它才会被执行
obj.myFun.call(db);
obj.myFun.apply(db);
obj.myFun.bind(db)();
2、call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象
2.1、call的参数是直接放进去的,第二第三第n个参数全都用逗号分隔,直接放到后面
obj.myFun.call(db,'1', ... ,'string' );
2.2、apply的所有参数都必须放在一个数组里面传进去
obj.myFun.apply(db,['1', ..., 'string' ]);
2.3、bind除了返回是函数以外,它 的参数和call 一样。
八、显示原型和隐式原型,手绘原型链,原型链是什么?为什么要有原型链
方法的原型(Function.prototype)是对象,
对象具有属性(proto)称为隐式原型,
对象的隐式原型指向构造该对象的构造函数的显式原型。
注意:
通过Function.prototype.bind方法构造出来的函数没有prototype属性。
Object.prototype.这个对象的是个例外,它的proto值为null。
二者的关系
隐式原型指向创建这个对象的函数的prototype
首先我们来看如何创建一个对象
a.通过对象字面量的方式。
var person={
name:"Tom"
}
通过对象字面量的方式创建的对象他的隐式原型指向Object.prototype。
b.通过new的方式创建
//创建一个构造函数
function person(name){
this.name=name
}
//创建一个构造函数的实例
var person1=new person;
构造函数function person本质上是由Function构造函数创建的,它是Function的一个实例。原型对象本质上是由Object构造函数创建的。内置函数Array Number等也是有Function构造函数创建的。
c.通过Object.creat()方式创建
var person={
name:"Tom"
}
const me = Object.create(person);
其中通过Object.creat(person)创建出来的对象他的隐式原型指向person。
但是本质上3种方法都是通过new的方式创建的。
九、实现继承的多种方式和优缺点
十、new 一个对象具体做了什么
使用关键字new创建新实例对象经过了以下几步:
1、创建一个新对象,如:var person = {};
2、新对象的proto属性指向构造函数的原型对象。
3、将构造函数的作用域赋值给新对象。(也所以this对象指向新对象)
4、执行构造函数内部的代码,将属性添加给person中的this对象。
5、返回新对象person。
十一、手写Ajax,XMLHttpRequest
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
// 通信成功时,状态值为4
if (xhr.readyState === 4){
if (xhr.status === 200){
console.log(xhr.responseText);
} else {
console.error(xhr.statusText);
}
}
};
xhr.onerror = function (e) {
console.error(xhr.statusText);
};
xhr.open('GET', '/endpoint', true);
xhr.send(null);
0,表示 XMLHttpRequest 实例已经生成,但是实例的open()方法还没有被调用。
1,表示open()方法已经调用,但是实例的send()方法还没有调用,仍然可以使用实例的setRequestHeader()方法,设定 HTTP 请求的头信息。
2,表示实例的send()方法已经调用,并且服务器返回的头信息和状态码已经收到。
3,表示正在接收服务器传来的数据体(body 部分)。这时,如果实例的responseType属性等于text或者空字符串,responseText属性就会包含已经收到的部分信息。
4,表示服务器返回的数据已经完全接收,或者本次接收已经失败。
十二、指出JS的宿主对象和原生对象的区别,为什么扩展JS内置对象不是好的做法?
宿主对象
“宿主”当然就是我们网页的运行环境,即“操作系统”和“浏览器”。
所有的BOM和DOM对象都是宿主对象。
原生对象
独立于宿主环境的ECMAScript实现提供的对象。与宿主无关,在javascript(远景浏览器)、nodejs(node平台)、jscript(ie浏览器)、typescript(微软平台)等等中均有这些对象。
十三、document load和document DOMContentLoaded两个事件的区别?
- 当 onload 事件触发时,页面上所有的DOM,样式表,脚本,图片,flash都已经加载完成了。
- 当 DOMContentLoaded 事件触发时,仅当外部脚本,样式表文件,DOM树完成,不包括图片,flash。
DOM文档加载的步骤为
- 解析HTML结构。
- 加载外部脚本和样式表文件。
- 解析并执行脚本代码。
- DOM树构建完成。//DOMContentLoaded
- 加载图片等外部文件。
- 页面加载完毕。//load
在第4步,会触发DOMContentLoaded事件。在第6步,触发load事件。
十四、typeof能够得到哪些值?
number, boolean, string, undefined, object, function.
十五、什么是“use strict”,好处和坏处
设立"严格模式"的优点:
1. 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
2. 消除代码运行的一些不安全之处,保证代码运行的安全;
3. 提高编译器效率,增加运行速度;
4. 为未来新版本的Javascript做好铺垫。
注:经过测试 IE6,7,8,9 均不支持严格模式。
缺点:
现在网站的 JS 都会进行压缩,一些文件用了严格模式,而另一些没有。这时这些本来是严格模式的文件,被 merge 后,这个串就到了文件的中间,不仅没有指示严格模式,反而在压缩后浪费了字节。
具体优点
1.使调试更加容易。那些被忽略或默默失败了的代码错误,会产生错误或抛出异常,因此尽早提醒你代码中的问题,你才能更快地指引到它们的源代码。
2.变量在赋值之前必须声明,防止意外的全局变量。如果没有严格模式,将值分配给一个未声明的变量会自动创建该名称的全局变量。这是JavaScript中最常见的错误之一。在严格模式下,这样做的话会抛出错误。
3.取消this值的强制转换。如果没有严格模式,引用null或未定义的值到 this 值会自动强制到全局变量。在严格模式下,引用 null或未定义的 this 值会抛出错误。严格模式下,this不会指向window
4.不允许重复的属性名称或参数值。当检测到对象中重复命名的属性,例如:
var object = {foo: "bar", foo: "baz"};
或检测到函数中重复命名的参数时,例如:
function foo(val1, val2, val1){})
严格模式会抛出错误,因此捕捉几乎可以肯定是代码中的bug可以避免浪费大量的跟踪时间。
5.使 eval() 更安全。在严格模式和非严格模式下, eval() 的行为方式有所不同。最显而易见的是,在严格模式下,变量和声明在 eval() 语句内部的函数不会在包含范围内创建(它们会在非严格模式下的包含范围中被创建,这也是一个常见的问题源)。
6.在 delete 使用无效时抛出错误。 delete 操作符(用于从对象中删除属性)不能用在对象不可配置的属性上。当试图删除一个不可配置的属性时,非严格代码将默默地失败,而严格模式将在这样的情况下抛出异常。
7.严格模式去除了with语句
8.不能修改arguments ,不能在函数内定义arguments变量 ,不能使用arugment.caller和argument.callee。因此如果你要引用匿名函数,需要对匿名函数命名。
十六、函数的作用域是什么?js 的作用域有几种?
var 函数作用域
let,const块作用域
十七、JS如何实现重载和多态
- 重载:
重载可认为是静态的多态,静态联编,发生在编译阶段;
重载就是一组具有相同名字、不同参数列表的函数(方法)。
重载,函数特征之一,表现为在一个类中同名不同参的方法分别被调用会产生不同的结果。
- 多态:
多态是动态的,动态联编,发生在运行阶段;
多态,面向对象特征之一,表现为不同对象调用相同方法会产生不同的结果。可以理解一个方法被不同实现后 展现不同的效果及状态。
静态的比动态的效率高,但动态的最大优点是多态性,提高代码复用性。
十八、原生事件绑定(跨浏览器),dom0和dom2的区别?
- DOM0级事件处理程序(属性绑定,兼容性好)
btn.onclick = 事件函数
btn.onclick = null; //删除事件处理程序
this指向当前元素
- DOM2级事件处理程序(函数绑定,兼容性不好)(IE9及IE9以上)
addEventListener()
removeEventListener()
参数:
要绑定的事件名
作为事件处理的函数
布尔值:true在捕获阶段调用事件处理程序;false在冒泡阶段调用
- IE事件处理程序
attachEvent()
detachEvent()
事件处理程序都被添加到冒泡阶段
4.区别:
如果定义了两个dom0级事件,dom0级事件会覆盖
dom2不会覆盖,会依次执行
dom0和dom2可以共存,不互相覆盖,但是dom0之间依然会覆盖
十九、给定一个元素获取它相对于视图窗口的坐标
document.getElementById("ele").getBoundingClientRect()
二十、如何实现图片滚动懒加载
js
var temp = "<ul class='container'>";
for (var i = 0; i < 10; i++){
temp += `<li><img src="${i}.png"/></li>`
}
for (var i = 0; i < 10; i++){
temp += "<li><img data-src='loading.png'/></li>"
}
temp += "</ul>"
document.body.innerHTML = temp;
var url = "http://192.168.0.0/page"
var isCanReq = true;
var urlArr = [];
var xhr = new XMLHttpRequest();
var pageNo = 2;
var pageSize = 10;
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr,status === 200) {
urlArr = JSON.parse(xhr.responseText);
var newtemp = "";
for (var i = 0; i < 10; i++){
newtemp += `<li><img data-src="${urlArr[i]}"/></li>`
}
}
}
}
document.getElementsByClassName("container")[0].addEventListener("scroll",(event) => {
var target = event.target;
if (target.scrollHeight - target.scrollTop < 100 && isCanReq){
isCanReq = false;
xhr.open("post", `${url}/${pageNo}/${pageSize}`, true);
var elesChildren = document.getElementsByClassName("container")[0].children;
for (var i = 10;i > 0;i++){
elesChildren[elesChildren.length - i].setAttribute("src", elesChildren[elesChildren.length - i].dataset.url)
}
xhr.send(null);
pageNo++;
}
})
css
ul > li {
margin:10px auto;
width:100px;
height:100px;
}
img {
width:100%;
height:100%;
}
二十一、js 的深拷贝
functon clone (val) {
if (JSON && JSON.parse){
return JSON.parse(JSON.stringify(val))
}
let res;
if (typeof val === "object") {
if (val === null) {
res = null;
} else if (val instanceof Array) {
res = [];
for (let i = 0;i < val.length;i++) {
res.push(clone(val[i]))
}
} else if (val instanceof Object) {
res = {};
for (const key in val) {
res[key] = clone(val[key]);
}
}
} else {
res = val;
}
return res;
}
二十二、web端cookie的设置和获取
var duration = +new Date() + (30 * 24 * 60 * 60 * 1000),
function setCookie(name, value) {
const me = this;
const exp = new Date(me.duration);
document.cookie = `${name}=${escape(value)};expires=${exp.toGMTString()}`;
}
function getCookie(name) {
if (!name) return "";
const reg = new RegExp(`(^|)${name}=([^;]*)(;|$)`);
const arr = document.cookie.match(reg);
if (arr) {
return decodeURI(arr[2]);
}
return "";
}
二十三、setTimeout和promise的执行顺序
先执行同步代码,遇到异步代码就先加入队列,然后按入队的顺序执行异步代码,最后执行setTimeout队列的代码。
二十四、navigator对象,location和history
Location 对象方法
属性 描述
assign() 加载新的文档。
reload() 重新加载当前文档。
replace() 用新的文档替换当前文档。
History 对象方法
方法 描述
back() 加载 history 列表中的前一个 URL。
forward() 加载 history 列表中的下一个 URL。
go()
二十五、js的垃圾回收机制
1、标记清除
这是javascript中最常用的垃圾回收方式。当变量进入执行环境是,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。
2、引用计数
另一种不太常见的垃圾回收策略是引用计数。引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。
这样的相互引用如果说很大量的存在就会导致大量的内存泄露。
二十六、内存泄漏的原因和场景
虽然JavaScript 会自动垃圾收集,但是如果我们的代码写法不当,会让变量一直处于“进入环境”的状态,无法被回收
1、给DOM对象添加的属性是一个对象的引用。范例:
var MyObject = {};
document.getElementById('myDiv').myProp = MyObject;
解决方法:
在window.onunload事件中写上: document.getElementById('myDiv').myProp = null;
2、给DOM对象用attachEvent绑定事件。范例:
function doClick() {}
element.attachEvent("onclick", doClick);
解决方法:
在onunload事件中写上: element.detachEvent('onclick', doClick);
二十七、DOM事件中target和currentTarget的区别
target在事件流的目标阶段;
currentTarget在事件流的捕获,目标及冒泡阶段。
只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的对象而currentTarget指向当前事件活动的对象(注册该事件的对象)(一般为父级)。this指向永远和currentTarget指向一致(只考虑this的普通函数调用)。
typeof 和 instanceof 区别,instanceof原理
typeof
用以获取一个变量的类型,typeof一般只能返回如下几个结 果:number,boolean,string,function,object,undefined
instanceof
用于 判断一个变量是否某个构造函数的实例
instanceof原理
检测对象A是不是另一个对象B的实例的原理是:查看对象B的prototype属性指向的原型对象是否在对象A的原型链上,若在则返回true,若不在则返回false。
obj.__proto__ === Object.prototype
二十八、js处理异常
try {
} catch (err) {
} finally{
}
二十九、js的设计模式知道那些
创建型
- 单体模式
单体模式思想在于保证一个特定类仅有一个实例,意味着当你第二次使用同一个类创建信对象时,应得到和第一次创建对象完全相同
var Universe;
(function(){
var instance;
Universe=function Universe(){
if(instance){
return instance;
}
instance=this;
this.xx="xx";
}
})();
var uni = new Universe();
Universe.prototype.a = 1
var uni2 = new Universe();
console.log(uni === uni2) //true
console.log(uni.a) //1
console.log(uni2.a) //1
console.log(uni.constructor === Universe); //true
- 工厂模式
工厂模式是为了创建对象。
例子
公共构造函数 CarMaker
名为factory的CarMaker静态方法来创建car对象
function CarMaker() {}
CarMaker.prototype.drive = function() {
return "I have " + this.doors + " doors";
}
CarMaker.compact = function() {
this.doors = 4;
}
CarMaker.convertible = function() {
this.doors = 2
}
CarMaker.suv = function() {
this.doors = 6;
}
CarMaker.factory = function(type) {
if (typeof CarMaker[type] !== "function") {
throw "Error"
}
if (typeof CarMaker[type].prototype.drive !== "function") {
CarMaker[type].prototype = new CarMaker();
}
var newCar = new CarMaker[type]();
return newCar;
}
var corolla = CarMaker.factory('compact');
console.log(corolla.drive()); //I have 4 doors
var WarriorFactory = function() {},
MageFactory = function() {},
ArcherFactory = function() {};
WarriorFactory.prototype.createCharacter = function() {
return new Warrior();
};
MageFactory.prototype.createCharacter = function() {
return new Mage();
};
ArcherFactory.prototype.createCharacter = function() {
return new Archer();
};
Player.prototype.play = function(role) {
var factory, character;
switch (role) {
case "战士":
factory = new WarriorFactory();
break;
case "法师":
factory = new MageFactory();
break;
case "弓箭手":
factory = new ArcherFactory();
break;
default :
factory = new WarriorFactory();
}
character = factory.createCharacter();
character.level();
character.gather();
character.fight();
};
结构型
- 装饰者模式
可以在运行时候添加附加功能到对象中,他的一个方便特征在于其预期行为的可定制和可配置特性。
例子 假设在开发一个销售商品的Web应用,每一笔信销售都是一个人新的 sale 对象。该对象“知道”有关项目的价格,并可以通过 getPrice() 方法返回加个。
根据不同情况,可以用额外的功能装饰此对象。
假设客户在魁北克省,买房需要支付联邦税和魁北克省税,则此时需要调用联邦税装饰者和魁北克省税装饰者。
function Sale(price) {
this.price = price;
this.decorateList = [];
}
Sale.decorators = {};
Sale.decorators.fedtax = {
getPrice: function(price) {
return price * 0.8; //对price进行处理
},
}
Sale.decorators.quebec = {
getPrice: function(price) {
return price * 0.7; //对price进行处理
},
}
Sale.decorators.money = {
getPrice: function(price) {
return "$" + price * 0.9; //对price进行处理
},
}
Sale.prototype.decorate = function(decorator) {
this.decorateList.push(decorator);
};
Sale.prototype.getPrice = function() {
var price = this.price;
this.decorateList.forEach(function(name) {
price = Sale.decorators[name].getPrice(price);
});
return price;
};
var sale = new Sale(100);
sale = sale.decorate("fedtax"); //联邦税
sale = sale.decorate("quebec"); //魁北克省税
sale = sale.decorate("money"); //转为美元格式
console.log(sale.getPrice()); //$50.4
行为型
- 迭代器模式
有一个包含某种数据集合的对象,该数据可能存储在一个复杂数据结构内部,而要提供一个简单方法讷讷感访问到数据结构中没一个元素。
next() 下一个
hasNext() 是否有下一个
reWind() 重置指针
current() 返回当前
var agg = (function() {
var index = 0;
var data = [1, 2, 3, 4, 5, 6];
var length = data.length;
return {
next: function() {
if (!this.hasNext()) {
return null;
}
var element = data[index];
index++;
return element;
},
hasNext: function() {
return index < length;
},
reWind: function() {
index = 0;
},
current: function() {
return data[index];
}
}
})();
while (agg.hasNext()) {
console.log(agg.next()); //1,2,3,4,5,6
}
agg.reWind(); //此时重置指针到0
- 策略模式
策略模式支持在运行时候选择算法。例如用在表单验证问题上,可以创建一个具有 validate() 方法的验证器对象,无论表单具体类型是什么,该方法都会被调用,
并且返回结果或者错误信息。
var validator = {
// 所有可以的验证规则处理类存放的地方,后面会单独定义
types: {},
// 验证类型所对应的错误消息
messages: [],
// 当然需要使用的验证类型
config: {},
// 暴露的公开验证方法
// 传入的参数是 key => value对
validate: function (data) {
var i, msg, type, checker, result_ok;
// 清空所有的错误信息
this.messages = [];
for (i in data) {
if (data.hasOwnProperty(i)) {
type = this.config[i]; // 根据key查询是否有存在的验证规则
checker = this.types[type]; // 获取验证规则的验证类
if (!type) {
continue; // 如果验证规则不存在,则不处理
}
if (!checker) { // 如果验证规则类不存在,抛出异常
throw {
name: "ValidationError",
message: "No handler to validate type " + type
};
}
result_ok = checker.validate(data[i]); // 使用查到到的单个验证类进行验证
if (!result_ok) {
msg = "Invalid value for *" + i + "*, " + checker.instructions;
this.messages.push(msg);
}
}
}
return this.hasErrors();
},
// helper
hasErrors: function () {
return this.messages.length !== 0;
}
};
//然后剩下的工作,就是定义types里存放的各种验证类了
// 验证给定的值是否不为空
validator.types.isNonEmpty = {
validate: function (value) {
return value !== "";
},
instructions: "传入的值不能为空"
};
// 验证给定的值是否是数字
validator.types.isNumber = {
validate: function (value) {
return !isNaN(value);
},
instructions: "传入的值只能是合法的数字,例如:1, 3.14 or 2010"
};
// 验证给定的值是否只是字母或数字
validator.types.isAlphaNum = {
validate: function (value) {
return !/[^a-z0-9]/i.test(value);
},
instructions: "传入的值只能保护字母和数字,不能包含特殊字符"
};
//使用的时候,我们首先要定义需要验证的数据集合,
//然后还需要定义每种数据需要验证的规则类型,代码如下:
var data = {
first_name: "Tom",
last_name: "Xu",
age: "unknown",
username: "TomXu"
};
validator.config = {
first_name: 'isNonEmpty',
age: 'isNumber',
username: 'isAlphaNum'
};
//最后获取验证结果
validator.validate(data);
if (validator.hasErrors()) {
console.log(validator.messages.join("\n"));
}
- 代理模式
在代理模式中,一个对象充当另外一个对象的接口
var package = function(receiver) {
this.receiver = receiver;
}
var seller = function(package) {
this.package = package;
this.send = function(gift) {
return package.receiver + "你的包裹:" + gift;
}
}
var express = function(package) {
this.package = package;
this.send = function(packageName) {
return new seller(package).send(packageName);
}
}
//调用
var ems = new express(new package("gary"));
console.log(ems.send("键盘")); //gary你的包裹:键盘
- 中介者模式
中介者模式可以让多个对象之间松耦合,并降低维护成本
例如:游戏程序,两名玩家分别给与半分钟时间来竞争决出胜负(谁按键的次数多胜出,这里玩家1按1,玩家2按0)
计分板(scoreboard)
中介者 (mediator)
中介者知道所有其他对象的信息。他与输入设备(此时是键盘)进行通信并处理键盘上的按键时间,之后还将消息通知玩家。玩家玩游戏同时(每一分都更新自己分数)还要
通知中介者他所做的事情。中介者将更新后的分数传达给计分板。
// 玩家对象构造函数
function Player(name) {
this.points = 0;
this.name = name;
}
Player.prototype.play = function() {
this.points += 1;
mediator.played();
};
// 中介者
var mediator = {
players: {}, //玩家对象
setup: function() { // 创建一个玩家home和一个玩家guest
var players = this.players;
players.home = new Player("home");
players.guest = new Player('guest');
},
played: function() { // 玩一次,就记录一次玩家和分数
var players = this.players;
var score = {
home: players.home.points,
guest: players.guest.points
}
},
keypress: function(e) { // 输入键盘,记录一次游戏,
//如果是1,记一分和游戏者home,如果是0,记一分和游戏者guest
e = e || window.event;
if (e.which === 49) { //or keycode 对应按键 1
mediator.players.home.play();
return;
}
if (e.which === 48) { // 对应按键 0
mediator.player.guest.play();
return;
}
},
}
//运行
mediator.setup(); // 创建一个中间者和一个玩家
window.onkeypress = mediator.keypress; // 在窗口绑定键盘keypress事件,
//每次输入键盘,记录一次游戏,如果是1,记一分和游戏者home,
//如果是0,记一分和游戏者guest
setTimeout(function() { //设置30秒游戏时间
window.onkeypress = null;
alert("game end");
}, 30000);
三十、轮播图的实现,以及轮播图组件开发,轮播10000张图片过程
<div class="swiper-container">
<div class="swiper-content">
<div class="swiper-item"><img data-src="" src="" /></div>
</div>
</div>
JS
function initSwiper() {
const urlArr = new Array(10000); // 图片URL数组
const eleContent = document.getElementsByClassName("swiper-content")[0];
let temp = "";
for (let i = 0;i < 10000;i++) {
temp += `<div class="swiper-item"><img data-src="${urlArr[i]}" src="" /></div>`;
}
eleContent.innerHTML = temp;
const eleChildren = eleContent.children;
let srcIndex = 0;
for (let i = 0;i < 3;i++) {
const eleImg = eleChildren[srcIndex].firstElementChild;
eleImg.setAttribute("src", eleImg.dataset.src);
srcIndex++;
}
setInterval(() => {
for (let i = 0;i < 3;i++) {
const eleImg = eleChildren[srcIndex].firstElementChild;
eleImg.setAttribute("src", eleImg.dataset.src);
srcIndex++;
}
}, 300);
startSwiper();
}
function startSwiper() {
const eleContent = document.getElementsByClassName("swiper-content")[0];
let marginLeftVal = +eleContent.style.marginLeft.replace(/px$/, "");
setInterval(() => {
marginLeftVal += 200;
eleContent.style.marginLeft = `${marginLeftVal}px`;
}, 200);
}
initSwiper()
CSS
.swiper-container {
width:200px;
height:100px;
overflow:hidden
}
.swiper-content {
display:flex;
}
.swiper-content.is-animated {
animation:margin-left 1s;
}
.swiper-item {
width:200px;
height:100px;
}
img {
width:100%;
height:100%;
}
requestAnimationFram
var eleDiv = document,getElementById("demo");
window.requestAnimatiion = window.requestAnimatiion ||
window.mozRequestAnimatiion ||
window.webkitRequestAnimatiion;
window.requestAnimationFrame(function fn(){
eleDiv.style.top = 1000 * Math.random() + "px";
window.requestAnimationFrame(fn);
})
三十一、websocket的工作原理和机制
WebSocket是一种双向通信协议,在建立连接后,WebSocket服务器和Browser/Client Agent都能主动的向对方发送或接收数据,就像Socket一样;
WebSocket需要类似TCP的客户端和服务器端通过握手连接,连接成功后才能相互通信。
相对于传统的HTTP每次请求-应答都需要客户端与服务端建立连接的模式,WebSocket是类似Socket的TCP长连接的通讯模式,一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。在客户端断开WebSocket连接或Server端断掉连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。
手指点击可以触控的屏幕时,是什么事件?
什么是函数柯里化?以及说一下JS的API有哪些应用到了函数柯里化的实现?(函数柯里化一些了解,以及在函数式编程的应用,最后说了一下JS中bind函数和数组的reduce方法用到了函数柯里化。)
JS代码调试
pjax是什么呢?
pjax是一种基于ajax+history.pushState的新技术,该技术可以无刷新改变页面的内容,并且可以改变页面的URL。pjax是ajax+pushState的封装,同时支持本地存储、动画等多种功能
具体可以看ajax与HTML5 history pushState/replaceState实例