1.1、请解释一下什么是闭包
当函数可以记住并访问所在的作用域时,就产生了闭包,即使函数是在当前作用域之外执行。闭包有如下特性:
a. JavaScript允许你使用在当前函数以外定义的变量
b. 即使外部函数已经返回,当前函数仍然可以引用在外部函数所定义的变量
c. 闭包可以更新外部变量的值
d. 用闭包模拟私有方法
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题。
在定时器、事件监听器、Ajax请求、跨窗口通信、Web Workers或者任何其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包!
1.2、call 和 apply 的区别是什么?
call 和 apply 就是为了改变函数体内部 this 的指向。
区别是从第二个参数起,call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。
当参数明确时用call与apply都行, 当参数不明确时可用apply给合arguments
1.3、如何使用原生 Javascript 代码深度克隆一个对象(注意区分对象类型)
用递归的方式克隆
function deepClone(obj) { var o,i,j,k; if(typeof(obj)!="object" || obj===null)return obj; if(obj instanceof(Array)) { o=[]; i=0;j=obj.length; for(;i<j;i++) { if(typeof(obj[i])=="object" && obj[i]!=null) { o[i]=arguments.callee(obj[i]); } else { o[i]=obj[i]; } } } else { o={}; for(i in obj) { if(typeof(obj[i])=="object" && obj[i]!=null) { o[i]=arguments.callee(obj[i]); } else { o[i]=obj[i]; } } } return o; } var scheduleClone = deepClone(schedule) scheduleClone.data[0].contactList.phone[0] = 99999999999 console.log('方法1 深度克隆') console.log(scheduleClone) console.log(JSON.stringify(schedule)) console.log(JSON.stringify(scheduleClone))
用js原生的json序列化的方式
var scheduleClone2 = JSON.parse(JSON.stringify(schedule)); console.log(scheduleClone2) scheduleClone2.data[0].contactList.phone[0] = 8888888 console.log(JSON.stringify(schedule)) console.log(JSON.stringify(scheduleClone2))
1.4、 jQuery中 $(′.class′)和$('div.class') 哪个效率更高?
jQuery内部使用Sizzle引擎,处理各种选择器。Sizzle引擎的选择顺序是从右到左,所以这条语句是先选.class,
第二个会直接过滤出div标签,而第一个就不会过滤了,将所有相关标签都列出。
1.5、实现输出document对象中所有成员的名称和类型
for(o in document){ type = Object.prototype.toString.call(document[o]); if(type =='[object Function]'){ console.log(o+":"+typeof o) } }
1.6、获得一个DOM元素的绝对位置
offsetTop:返回当前元素相对于其 offsetParent 元素的顶部的距离
offsetLeft:返回当前元素相对于其 offsetParent 元素的左边的距离
getBoundingClientRect():返回值是一个DOMRect对象,它包含了一组用于描述边框的只读属性——left、top、right和bottom,属性单位为像素
1.7、如何利用JS生成一个table?
首先是用createElement创建一个table,再用setAttribute设置table的属性,
然后用for循环设置tr和td的内容,用appendChild拼接内容,设置td的时候还用到innerHTML和style.padding。
var row, cell, table = document.createElement("table"); table.setAttribute("border", 1); for (var i = 0; i < 10; i++) { row = document.createElement("tr"); table.appendChild(row); for (var j = 0; j < 10; j++) { cell = document.createElement("td"); cell.style.padding = "10px"; cell.innerHTML = "单元格内容"; row.appendChild(cell); } }; table.appendChild(row); document.body.appendChild(table);
1.8、实现预加载一张图片,加载完成后显示在网页中并设定其高度为50px,宽度为50px
var img = new Image(); img.src = "1.jpg"; img.onload = function() { img.height = 50; img.width = 50; } document.body.appendChild(img);
1.9、假设有一个4行tr的table,将table里面tr顺序颠倒
先是通过table.tBodies[0].rows获取到当前tbody中的行,接下来是两种方法处理。获取到的行没有reverse这个方法。
1.第一种是将这些行push到另外一个数组中
var tableTemple=document.querySelector("#tableTemple"); var table_rows=tableTemple.tBodies[0].rows; var newAry=[]; for(var i=0;i<table_rows.length;i++){ newAry.unshift(table_rows[i]); }; for(i in newAry){ tableTemple.tBodies[0].appendChild(newAry[i]); }
2.第二种是用Array.prototype.slice.call()将那些行变成数组,接着用reverse倒叙,table再appendChild。
var tableTemple=document.querySelector("#tableTemple"); var table_rows=tableTemple.tBodies[0].rows; var trs = Array.prototype.slice.call(table_rows, 0);Array.prototype.slice.call(arguments)//能将具有length属性的对象转成数组 trs.reverse(); for(var i=0;i<trs.length;i++){ tableTemple.tBodies[0].appendChild(trs[i]); }
1.10、模拟一个HashTable类,一个类上注册四个方法:包含有add、remove、contains、length方法
先是在构造函数中定义一个数组,然后用push模拟add,splice模拟remove。
四个方法都放在了prototype上面。
function HashTable(){ this.value=[]; }; HashTable.prototype.add=function(val){ this.value.push(val); }; HashTable.prototype.remove=function(index){ this.value.splice(index,1) } HashTable.prototype.contains=function(val){ var aValue=this.value; for(i in aValue){ if(val==aValue[i]){ return true; } } return false; } HashTable.prototype.length = function() { return this.value.length; }
1.11、Ajax读取一个XML文档并进行解析的实例
a. 初始化一个HTTP请求,IE以ActiveX对象引入。 后来标准浏览器提供了XMLHttpRequest类,它支持ActiveX对象所提供的方法和属性
b. 发送请求,可以调用HTTP请求类的open()和send()方法
c. 处理服务器的响应,通过http_request.onreadystatechange = nameOfTheFunction。来指定函数
//创建请求对象 function create() { if (window.XMLHttpRequest) { // Mozilla, Safari, ... http_request = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE http_request = new ActiveXObject("Microsoft.XMLHTTP"); } return http_request; } //发起请求 function makeRequest(url) { http_request = create(); http_request.onreadystatechange = alertContents; http_request.open('GET', url, true); http_request.send(null); } //响应函数 function alertContents() { if (http_request.readyState == 4) { if (http_request.status == 200) { var xmldoc = http_request.responseXML; var root_node = xmldoc.getElementsByTagName('root').item(0); alert(root_node.firstChild.data); } else { alert('There was a problem with the request.'); } } }
1.12、JS如何实现面向对象和继承机制?
创建对象方法:
a. 利用json创建对象
b. 使用JavaScript中的Object类型
c. 通过创建函数来生成对象
继承机制:
a. 构造函数绑定,使用call或apply方法,将父对象的构造函数绑定在子对象上
b. prototype模式,继承new函数的模式
c. 直接继承函数的prototype属性,对b的一种改进
d. 利用空对象作为中介
e. 在ECMAScript5中定义了一个新方法Object.create(),用于创建一个新方法
f. 拷贝继承,把父对象的所有属性和方法,拷贝进子对象,实现继承。
1.13、JS模块的封装方法,比如怎样实现私有变量,不能直接赋值,只能通过公有方法
a. 通过json生成对象的原始模式,多写几个就会非常麻烦,也不能反映出它们是同一个原型对象的实例
b. 原始模式的改进,可以写一个函数,解决代码重复的问题。同样不能反映出它们是同一个原型对象的实例
c. 构造函数模式,就是一个普通函数,不过内部使用了this变量,但是存在一个浪费内存的问题。
d. Prototype模式,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承,可以把那些不变的属性和方法,直接定义在prototype对象上。Prototype模式的验证方法:isPrototypeOf()、hasOwnProperty()和in运算符。
//通过json生成对象的原始模式 var cat1 = {}; // 创建一个空对象 cat1.name = "大毛"; // 按照原型对象的属性赋值 cat1.color = "黄色"; var cat2 = {}; cat2.name = "二毛"; cat2.color = "黑色"; //原始模式的改进 function Cat2(name,color){ return { name:name, color:color } } //构造函数模式 function Cat3(name,color){ this.name=name; this.color=color; } //Prototype模式 function Cat4(name,color){ this.name = name; this.color = color; } Cat4.prototype.type = "猫科动物"; Cat4.prototype.eat = function(){alert("吃老鼠")}; //Prototype验证方法 var human = function() {} var socrates = Object.create(human); console.log(human.isPrototypeOf(socrates)); //=> true //console.log(socrates.prototype.isPrototypeOf(human)); console.log(socrates instanceof human); //=> false //console.log(socrates.prototype)
1.14、对this指针的理解,可以列举几种使用情况?
this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
this指的是:调用函数的那个对象。
a. 纯粹的函数调用,属于全局性调用,因此this就代表全局对象Global。
b. 作为对象方法的调用,这时this就指这个上级对象。
c. 作为构造函数调用,就是通过这个函数new一个新对象(object)。这时,this就指这个新对象。
d. apply与call的调用,它们的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。
//a. 纯粹的函数调用 function test1() { this.x = 1; console.log(this.x); } test1(); // 1 var y = 1; function test2() { console.log(this.y); } test2(); // 1 var z = 1; function test3() { this.z = 2; } test3(); console.log(z); //2 //b. 作为对象方法的调用 function test4() { console.log(this.x); } var o = {}; o.x = 10; o.m = test4; o.m(); // 10 //c.作为构造函数调用 function test5() { this.x = 1; } var o = new test5(); console.log(o.x); // 1 //d.apply与call的调用 var x = 0; function test6() { console.log(this.x); } var o = {}; o.x = 1; o.m = test6; o.m.apply(o); //1
1.15、在JavaScript中,常用的绑定事件的方法有哪些?
a. 在DOM元素中直接绑定,DOM元素,可以理解为HTML标签,onXXX="JavaScript Code"。
b. 在JavaScript代码中绑定,elementObject.onXXX=function(){},通称为DOM0事件系统。
c. 绑定事件监听函数,标准浏览器使用 addEventListener() ,IE11以下版本attachEvent() 来绑定事件监听函数,通称为DOM2事件系统。
1.16、解释下javascript的冒泡和捕获
<div id="click1">
<div id="click2">
<div id="click3">事件</div>
</div>
</div>
a. Netscape主张元素1的事件首先发生,这种事件发生顺序被称为捕获型
b. 微软则保持元素3具有优先权,这种事件顺序被称为冒泡型
c. W3C选择了一个择中的方案。任何发生在w3c事件模型中的事件,首是进入捕获阶段,直到达到目标元素,再进入冒泡阶段
事件监听函数addEventListener()的第三个参数就是控制方法是捕获还是冒泡
click1 = document.getElementById("click1"); click2 = document.getElementById("click2"); click3 = document.getElementById("click3"); click1.addEventListener("click", returnTarget, false); click2.addEventListener("click", returnTarget, false); click3.addEventListener("click", returnTarget, false); function returnTarget(event) { console.log(event.currentTarget.id); //event.stopPropagation(); }
1.17、jQuery的特点
a. 一款轻量级的js库
b. 丰富快速的DOM选择器
c. 链式表达式
d. 事件、样式、动画等特效支持
e. Ajax操作封装,支持跨域
f. 跨浏览器兼容
g. 插件扩展开发
1.18、Ajax有哪些好处和弊端?
优点:
a. 无刷新更新数据
b. 异步与服务器通信
c. 前端和后端负载平衡
d. 基于标准被广泛支持
e. 界面与应用分离
缺点:
a. AJAX干掉了Back和History功能,即对浏览器机制的破坏
b. AJAX的安全问题
c. 对搜索引擎支持较弱
d. 违背URL和资源定位的初衷
参考《AJAX工作原理及其优缺点》
1.19、null和undefined的区别?
null:
a. null是一个表示"无"的对象,转为数值时为0
b. null表示"没有对象",即该处不应该有值。
undefined:
a. undefined是一个表示"无"的原始值,转为数值时为NaN。
b. undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。
1.20、new操作符具体干了什么呢?
a. 一个新对象被创建。它继承自函数原型
b. 构造函数被执行。执行的时候,相应的传参会被传入
c. 上下文(this)会被指定为这个新实例
d. 如果构造函数返回了一个“对象”,那么这个对象会取代整个new出来的结果
1.21、js延迟加载的方式有哪些?
a. 将script节点放置在最后</body>之前
b. 使用script标签的defer和async属性,defer属性为延迟加载,是在页面渲染完成之后再进行加载的,而async属性则是和文档并行加载
c. 通过监听onload事件,动态添加script节点
d. 通过ajax下载js脚本,动态添加script节点
1.22、如何解决跨域问题?
a. JSONP(JSON with Padding),填充式JSON
b. iframe跨域
c. HTML5的window.postMessage方法跨域
d. 通过设置img的src属性,进行跨域请求
e. 跨域资源共享(CORS),服务器设置Access-Control-Allow-OriginHTTP响应头之后,浏览器将会允许跨域请求
1.23、documen.write和 innerHTML的区别
write:
a. 改变 HTML 输出流
b. 当在文档加载之后使用 document.write(),这会覆盖该文档。例如onload事件中
c. 输入css的style标签能改变样式,例如document.write("<style>b{color:red;font-weight:bold;}</style>");
innerHTML:
a. 改变 HTML 内容
b. 输入css的style标签不能改变样式。也是能改变样式的
1.24、哪些操作会造成内存泄漏?
a. 当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露。
b. 在IE中,如果循环引用中的任何对象是 DOM 节点或者 ActiveX 对象,垃圾收集系统则不会处理。
c. 闭包可以维持函数内局部变量,使其得不到释放。
d. 在销毁对象的时候,要遍历属性中属性,依次删除,否则会泄漏。
1.25、JavaScript中的变量声明提升?
函数声明和变量声明总是被JavaScript解释器隐式地提升到包含他们的作用域的最顶端。
function优先声明于var。
函数表达式中只会提升名称,函数体只有在执行到赋值语句时才会被赋值。
function foo() {
bar();
var x = 1;
}
function foo() {//等同于
var x;
bar();
x = 1;
}
function test() {
foo(); // TypeError "foo is not a function"
bar(); // "this will run!"
var foo = function () { }// 函数表达式被赋值给变量'foo'
function bar() { }// 名为'bar'的函数声明
}
1.26、如何判断当前脚本运行在浏览器还是node环境中?
通过判断Global对象是否为window,如果是window,当前脚本运行在浏览器中
1.27、什么是 "use strict"
ECMAscript 5添加了第二种运行模式:"严格模式"(strict mode)
设立"严格模式"的目的,主要有以下几个:
a. 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
b. 消除代码运行的一些不安全之处,保证代码运行的安全;
c. 提高编译器效率,增加运行速度;
d. 为未来新版本的Javascript做好铺垫。
注:经过测试IE6,7,8,9均不支持严格模式
1.28、eval是做什么的?
eval()函数可计算某个字符串,并执行其中的的 JavaScript 代码。
eval()是一个顶级函数并且跟任何对象无关。
如果字符串表示了一个表达式,eval()会对表达式求值。如果参数表示了一个或多个JavaScript声明, 那么eval()会执行声明。
1.29、JavaScript原型,原型链 ?
原型:
a. 原型是一个对象,其他对象可以通过它实现属性继承。
b. 一个对象的真正原型是被对象内部的[[Prototype]]属性(property)所持有。浏览器支持非标准的访问器__proto__。
c. 在javascript中,一个对象就是任何无序键值对的集合,如果它不是一个主数据类型(undefined,null,boolean,number,string),那它就是一个对象。
原型链:
a. 因为每个对象和原型都有一个原型(注:原型也是一个对象),对象的原型指向对象的父,而父的原型又指向父的父,我们把这种通过原型层层连接起来的关系称为原型链。
b. 这条链的末端一般总是默认的对象原型。
a.__proto__ = b;
b.__proto__ = c;
c.__proto__ = {}; //default object
{}.__proto__.__proto__; //null
1.30、JQuery与jQuery UI 有啥区别?
jQuery是一个js库,主要提供的功能是选择器,属性修改和事件绑定等等。
jQuery UI则是在jQuery的基础上,利用jQuery的扩展性,设计的插件。提供了一些常用的界面元素,诸如对话框、拖动行为、改变大小行为等等
1.31、jQuery的源码看过吗?能不能简单说一下它的实现原理?
jQuery给我们带来了一个简洁方便的编码模型(1>创建jQuery对象;2>直接使用jQuery对象的属性/方法/事件),
一个强悍的dom元素查找器($),插件式编程接口(jQuery.fn),以及插件初始化的”配置”对象思想
1.32、jQuery 中如何将数组转化为json字符串
在jQuery1.8.3中有个方法“parseJSON”,在这个方法中会做从string转换为json。
如果当前浏览器支持window.JSON,那就直接调用这个对象中的方法。
如果没有就使用( new Function( "return " + data ) )();执行代码返回。
1.33、请写出console.log中的内容
var msg = 'hello';//顶级作用域window下有个变量msg
function great(name, attr) {
var name = 'david';
var greating = msg + name + '!';
var msg = '你好';
for (var i = 0; i < 10; i++) {
var next = msg + '你的id是' + i * 2 + i;
}
console.log(arguments[0]);// david
console.log(arguments[1]);// undefined
console.log(greating);// undefineddavid!
console.log(next);// 你好你的id是189
}
great('Tom')
1.35、请说明下下面代码的执行过程
var t=true;
window.setTimeout(function(){
t=false;
},1000);
while(t){
console.log(1);
}
alert('end');
a. JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序
b. setTimeout是异步线程,需要等待js引擎处理完同步代码(while语句)之后才会执行,while语句直接是个死循环,js引擎没有空闲,不会执行下面的alert,也不会插入setTimeout。我在chrome中执行在线代码,最后浏览器是终止死循环执行alert。
c. JavaScript的工作机制是:当线程中没有执行任何同步代码的前提下才会执行异步代码,setTimeout是异步代码,所以setTimeout只能等js空闲才会执行,但死循环是永远不会空闲的,所以setTimeout也永远不会执行。
1.36、输出今天的日期,以YYYY-MM-DD的方式,比如今天是2014年9月26日,则输出2014-09-26
function getCurrentDate(){
var timeStr = '-';
var curDate = new Date();
var curYear =curDate.getFullYear(); //获取完整的年份(4位,1970-????)
var curMonth = curDate.getMonth()+1; //获取当前月份(0-11,0代表1月)
var curDay = curDate.getDate(); //获取当前日(1-31)
var curWeekDay = curDate.getDay(); //获取当前星期X(0-6,0代表星期天)
var curHour = curDate.getHours(); //获取当前小时数(0-23)
var curMinute = curDate.getMinutes(); // 获取当前分钟数(0-59)
var curSec =curDate.getSeconds(); //获取当前秒数(0-59)
var Current= curYear+timeStr+curMonth+timeStr+curDay+' '+curHour+':'+curMinute+':'+curSec;
console.log(Current);
return Current;
}
getCurrentDate()
1.37、Javascript中callee和caller的作用?
arguments.callee属性包含当前正在执行的函数。
Function.caller返回一个对函数的引用,该函数调用了当前函数。
1.39、JS异步编程方式有几种?
a. 回调函数
b. 事件监听
c. 发布订阅
d. promise
1.40、请说说在JavaScript引用类型和值类型的理解
值类型:存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。即Undefined、Null、Boolean、Number 和 String。
引用类型:存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存处。即对象、数组
var a = {n:1};
var b = a;
a.x = a = {n:2};
console.log(a.x);// --> undefined
console.log(b.x);// --> [object Object]
1.41、请解释一下JavaScript中的作用域和作用域链
变量的作用域(scope):程序源代码中定义这个变量的区域。
作用域链:是一个对象列表或链表,这组对象定义了这段代码“作用域中”的变量。查找变量会从第一个对象开始查找,有则用,无则查找链上的下一个对象。
1.42、用例子来讲事件委托过程
<input type="button" name="" id="btn" value="添加" /> <ul id="ul1"> <li>111</li> <li>222</li> <li>333</li> <li>444</li> </ul>
window.onload = function(){ var oBtn = document.getElementById("btn"); var oUl = document.getElementById("ul1"); var aLi = oUl.getElementsByTagName('li'); var num = 4; //事件委托,添加的子元素也有事件 oUl.onmouseover = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == 'li'){ target.style.background = "red"; } }; oUl.onmouseout = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == 'li'){ target.style.background = "#fff"; } }; //添加新节点 oBtn.onclick = function(){ num++; var oLi = document.createElement('li'); oLi.innerHTML = 111*num; oUl.appendChild(oLi); }; }
1.43、数组去重
方法1:ES6 Set函数
var strAry=[1, 2, 3, 4, 4]; var set = new Set(strAry); console.log(set)//[1,2,3,4]
方法2:利用对象属性存在的特性,如果没有该属性则存入新数组。
var strAry=[1, 2, 3, 4, 4];
function unique(arr) {
var obj={} var newArr=[] for (let i = 0; i < arr.length; i++) { if (!obj[arr[i]]) { obj[arr[i]] = 1 newArr.push(arr[i]) } } return newArr }
console.log(unique(strAry))
方法2:利用数组的indexOf下标属性来查询。
var strAry=[1, 2, 3, 4, 4];
function unique4(arr) { var newArr = [] for (var i = 0; i < arr.length; i++) { if (newArr.indexOf(arr[i])===-1) { newArr.push(arr[i]) } } return newArr } console.log(unique4(strAry))
1.44、跨域解决方案
1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域