前端面试题二
第二阶段 JavaScript、jQuery、ECMAScript、JSON
1、jQuery this与this区别
$(this)是jquery对象,是用jQuery包装过的javascript中的this,能调用jquery的方法,例如click(), keyup()
this则是html元素对象,能调用元素属性,例如this.id,this.value。
例如假设已经使得this和$(this)都指向了input对象了,若要获得input的值,可以this.value,但$(this)就得$(this).val()
6、说几条写JavaScript的基本规范
1)减少全局污染
javascript使用function来管理自己的作用域,定义在函数内的变量对外是不可见的。对于javascript的执行环境来说一般都有一个全局对象,在所有的函数外可以用this来指引,例如在浏览器端是window。当在全局执行环境下定义过多的变量会造成很多问题,自己定义的变量被其他人定义的同名变量覆盖掉,或者你未来定义的变量把你过去定义的同名变量也覆盖掉(同名变量覆盖);过多的全局变量放在执行栈里还会造成内存的浪费
(1)声明变量忘记var-----使得该变量是一个全局变量,有可能其他地方修改覆盖它或者将其他地方的变量覆盖
做法:所有的变量都使用“var”,并且尽量都声明在函数体的头部,这样一目了然
(2)变量名提升-----javascript中函数内所有使用var声明的变量都会提升到函数体头部
做法:在函数体内所有的var声明的变量都拿到函数体顶部,以免造成不必要的错误
(3)for循环----在for循环里面打算延时输出每一个i的值,但最后输出的值都一样
for(var i =0;i<3;i++){
setTimeout(function(){console.log(i)},1000)
}
最后输出3个值都是3
如果myarray是读取的DOM的节点,那么每一次循环都要去DOM里选取节点再做判断,非常影响性能
for (var i = 0; i < myarray.length; i++) {
//logic
}
做法:将DOM节点的长度用一个变量缓存
for (var i = 0, max = myarray.length; i < max; i++) {
// logic
}
(4)用"==="代替"=="-----前者是严格判断,后者会提前进行隐式的类型转换
(5)统一缩进大小,任何用花括号括起来并换行的都进行缩进
(6)花括号----{}
for循环或者if判断等,即使只有一行,也要换行并用{}括起来
(7)命名规则
构造器函数首字母大写;变量用驼峰式;常量全用大写字母;私有函数用下划线开头
(8)写注释
你写的代码别人不一定看得懂,你写的将来你也不一定轻易能看懂,所以良好的注释习惯可以事半功倍
7、null,undefined 的区别?
null: null类型,代表一个空对象指针,使用typeof运算得到 “object”,所以可以认为它是一个特殊的对象值
undefined: Undefined类型,当声明一个变量未初始化时,得到的就是undefined;typeof得到的 值为'undefined'没有返回值的函数返回undefined,没有实参的形参也是undefined
我的理解就是: undefined是访问一个未初始化的变量时返回的值,而null是访问一个尚未存在的对象时所返回的值。因此,可以把undefined看作是空的变量,而null看作是空的对象
在定义一个想保存对象的变量时,就可以让该变量先保存null值,这样既能体现null是一个空指针对象,也能更好的区分null和undefined
函数没有传参的时候,参数是undefined;如果函数没有返回值,那么返回值为undefined
7、== 和 ===的区别
===:为恒等符号,当等号两边的值为相同类型的时候,直接比较两边的值,值相同返回true,否则返回false;两边的值的类型不一样,返回false
==:为等值符号,当等号两边的值的类型一样的时候,直接比较值是否相等,相等返回true,否则false;如果等号两边的值类型不一样,则调用toNumber()转换成数值类型,在比较值是否相同,转化规则:
// 不同类型调用toNumber()转化后的值
undefined:NaN
null:0
boolean:true-1,false-0
number:number
string:纯数字字符串返回数值,如果字符串包含字符返回值false
object:调用valueOf toString()
// 任何值与NaN比较都为false
console.log( 0 == NaN ); //false
// 0 [] {} '' false undefiend null
// 0与虚值得比较
console.log( 0 == false ); //true
console.log( 0 == '' ); //true
console.log( 0 == [] ); //true
console.log( 0 == {} ); //false
console.log( 0 == undefined ); //false
console.log( 0 == null ); //false
// 空字符串与一些虚值的比较
console.log( '' == 0 ); //true
console.log( '' == [] ); //true
console.log( '' == false ); //true
console.log( '' == {} ); //false
console.log( '' == null ); //false
console.log( '' == undefined ); //false
console.log( null == undefined ); //true
8、50 == '50'的转换过程
首先,这里是等值符号,然后两边的值类型不一样,需要先转换为number,左边为number类型,所以不需要转换,而右边是string类型,需要转转成number,由于是纯的数值字符串,所以变成50,然后两边的值相等,所以输出为true
8、谈谈对this对象的理解?javascript中的this对象是如何工作的
this对象的几个定义
ES6之前,this总是指向一个对象,具体指向那个对象是在运行时基于函数的执行环境动态绑定的,而不是函数被声明时的环境
ES6中的箭头函数中this有所区别:箭头函数中的this指向声明时的外围的第一个不是箭头函数的对象;全局环境下的this=unedfined
this的指向
1)作为对象的方法调用---当函数被视为某个对象的方法调用时,this等于那个对象
2)作为普通函数调用----在全局函数中,this指向全局对象,在浏览器中指向window
3)构造器调用
4)Function.prototype.call或Function.prototype.apply调用
注意:匿名函数的执行环境具有全局性,因此在window中this对象通常指向window
● 每个函数在被调用的过程中都会自动取得两个特殊变量:this和arguemtns。内部函数在搜索这两个变量的时候,只会搜索到其活动对象为止,因此永远不可能访问外部函数中的这两个变量。
● this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
67、new操作符具体做了什么?
1)创建一个空对象,同时还继承了该函数的原型
2)this指向该对象
3)如果函数没有明确返回值,则隐式的返回 this
4)执行构造函数(this.constructor)
关于this的栗子的链接:https://www.cnblogs.com/qqqiangqiang/p/5316973.html
4、请解释为什么接下来这段代码不是 IIFE (立即调用的函数表达式)?要做哪些改动使它变成 IIFE?
function foo(){ }();
IIFE的表示是将函数包含在一个括号内,变成为一个表达式,随后跟着一个(),就立即执行这个函数了,修改成如下形式:
(function fn(){..})()
IIFE(立即调用的函数表达式)的作用
● 创建作用域,内部保存一些大量临时变量的代码,防止命名冲突
● 一些库的外层用这种形式包起来,防止作用域污染
● 运行一些只执行一次的代码
12、有一个按钮是异步生成的,怎样对它进行事件绑定
用事件代理的方式,将事件绑定在父元素对象上,通过event.target判断按钮是否已经生成,从而实现相应事件
12、高阶函数
至少满足下面的条件之一才是高阶函数:
1)函数可以作为参数传递
2)函数可以作为返回值输出
高阶函数 的应用场景
A、函数作为参数传递
1)回调函数----异步请求;当一个函数不适合执行一些请求,可以吧一个函数委托给另外一个函数执行
2)Array.prototype.sort(fn)------fn用于判断sort是升序还是降序排列
B、函数作为返回值
1)判断数据的类型----利用Object.prototype.toString.call(obj)
2)单例模式-----函数作为参数,函数的返回值为函数
3)高阶函数实现AOP(Facing-oriented Programming,面向切面编程)------把一个函数动态织入另一个函数中,可以扩展Function.prototype
C、函数柯里化currying/uncurrying
D、函数节流------使用延时函数降低函数调用的频率
E、分时函数-----利用定时器分时加载数据到页面
F、惰性加载函数-----第一次进入函数或进行一次分支判断,然后重写该函数,以后调用就是包裹正确逻辑的函数
12、JavaScript中的作用域、作用域链、变量声明提升
变量的声明提升:就是把变量的声明提升到当前作用域的最前面
函数的声明提升:就是把整个函数提升到当前作用域的最前面(位于前置的变量声明后面)
Javascript中的作用域及作用域链
执行环境:定义了变量或者函数有权访问的其他的数据,决定了他们各自的行为
变量的作用域就是变量和函数的可访问范围,或者说变量或函数起作用的区域(全局作用域,函数作用域即局部作用域)
1.javascript函数的作用域:
函数内的区域,就是这个函数的作用域,变量和函数在这个区域都可以访问操作。最外层函数外的区域叫全局作用域,函数内的区域叫局部作用域。
2.javascript变量的作用域:
在源代码中变量所在的区域,就是这个变量的作用域,变量在这个区域内可以被访问操作。在全局作用域上定义的变量叫全局变量,在函数内定义的变量叫局部变量
简单地理解,JS源代码被函数{ }划分成一块一块的区域,这些区域换个身份就是某函数或某变量的作用域,变量的作用域和函数的作用域在源代码中有可能指的是同一块区域。
作用域链的详解:http://www.jb51.net/article/87964.htm
作用域链:当代码在一个环境中执行时,会创建变量对象的一个作用域链。
作用域链的用途:保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window
对象即被终止,作用域链向下访问变量是不被允许的。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开始的时候只包含一个变量,即arguments对象。作用域链中的下一个变量对象来自包含(外部)环境,则在下一个变量对象则来自下一个包含环境,这样一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。
10、变量的生命周期
1)全局变量:其生命周期是永久的,除非主动销毁这个全局变量
2)在函数内用var声明的局部变量:它们会随着函数调用的结束而销毁
10、什么是闭包(closure)? 为什么要用它? 如何使用?
闭包的形成和作用域以及生命周期有关
闭包的定义
● 当某个函数调用时会创建一个执行环境以及作用域链,然后根据arguments和其它命名参数初始化形成活动对象。
● 在外部函数调用结束后,其执行环境与作用域链被销毁,但是其活动对象保存在了闭包之中,最后在闭包函数调用结束后才销毁。
● 简单的说,闭包就是能够读取其他函数内部变量的函数。在js中,闭包是指有权访问另一个函数作用域中的变量的函数。
如何使用
A函数内部的B函数调用了A中的变量,将A函数内部的B函数作为A函数的返回值返回;
在一个函数内部又定义了一个闭包;
为什么要用它?有什么作用
匿名自执行函数;
更优雅,更简洁的表达出代码;
在某些方面提升代码的执行效率;
实现封装;
实现面向对象中的对象;
缓存
最大的用处有两个:
1)可以读取其他函数内部的变量
2)让这些变量的 值始终保存在内存中(闭包使得Javascript的垃圾回收机制GC不会收回外部函数所占用的资源,因为外部函数的内部函数的执行需要依赖外部函数中的变量。 )
看下面这个例子:
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
使用闭包的注意事项
(1) 问题:由于闭包会使得函数中的变量都被保存在内存中,内存消耗大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能会造成内存泄露。
解决方法:在退出函数之前,将不使用的局部变量全部删掉
(2) 问题:闭包会在父函数外部,改变父函数内部变量的值。
如果把父函数当做对象使用,把闭包当做公用方法,把内部变量当做私有属性,此时不要随便改变父函数内部变量的值。
//----代码一
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){ //js中,函数就是作用域
return this.name; // 闭包 + this,造成的问题,函数中的this不能访问到作用域外的变量
};
}
};
alert(object.getNameFunc()()); //this的作用域是在函数执行的时候确定,因为匿名函数是一个全局变量,因此this指向的是window对象
// 因此输出结果是:The Window
//-----代码二
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this; //--this指向object,保留了对象object在that里面,
return function(){
return that.name; //这里输出对象object的name属性的值
};
}
};
alert(object.getNameFunc()()); // My Object
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);
//问:三行a,b,c的输出分别是什么?
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
//undefined 0 0 0
var b = fun(0).fun(1).fun(2).fun(3);
//undefined 0 1 2
var c = fun(0).fun(1); c.fun(2); c.fun(3);
//undefined 0 1 1
对于js中一些变量操作的取值问题
● 匿名自执行函数
a) 在js中声明变量的时候,如果不加上var关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处,比如:别的函数可能误用这些变量;造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的)。
b) 有的函数只需要执行一次,其内部变量无需维护,比如UI的初始化,那么我们可以使用闭包:我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在执行完后很快就会被释放,关键是这种机制不会污染全局对象
● 缓存---减少全局变量,减少出错的几率
再来看一个例子,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。
● 封装
即将函数A内部的函数B作为A函数的返回值,这样在A函数外即可以访问到A函数内部的私有变量。
● 实现面向对象中的对象
虽然JavaScript中没有类这样的机制,但是通过使用闭包,我们可以模拟出这样的机制
带例子解释的链接:https://blog.****.net/sunlylorn/article/details/6534610
用闭包实现一个函数,每调用一次这个函数,变量的值就会自增
// 方法一
// var add = (function () {
// var counter = 0;
// return function () {return counter += 1;}
// })();
// console.log(add()); //1
// console.log(add()); //2
// console.log(add()); //3
// 方法二
function autoAddOne(){
var sum = 0;
return function(){
sum = sum +1;
return sum;
};
}
var tempFunc = autoAddOne();
console.log(tempFunc())
console.log(tempFunc())
方法二中之所以每次调用tempFunc()都会自增1,是因为在autoAddOne()中返回一个函数,该函数调用了autoAddOne()里面的变量,同时由于var声明的变量有提升的作用,tempFunc是一个全局变量,所以对返回函数的内存不释放,由于返回函数又调用了autoAddOne中的变量,所以这个函数所占用的内存也不释放
用什么方式可以实现外部函数访问到闭包里面的变量
将函数的返回值设定为一个对象,对象里面有一个属性是函数,该函数返回其里面定义的变量的值
function f1(){
var s1 = 1;
return {
f2:function(){
var temp = 1;
return temp;
}
};
}
var a1 = f1();
console.log(a1.f2());
使用闭包实现一个once函数
function once() {
var isFirst = true;
return function () {
if (isFirst) {
isFirst = !isFirst; //闭包对外部函数的变量的引用不释放,从而使引用的变量存放在内存中
console.log('test');
}
};
};
var b = once();
b(); // 'test'
b(); // nothing
//或者写成
var once = (function(){
var isFirst = true;
return function () {
if (isFirst) {
isFirst = !isFirst; //闭包对外部函数的变量的引用不释放,从而使引用的变量存放在内存中
console.log('test');
}
};
})()
在代理单例模式中,就使用闭包实现单例模式
let getSingle = function(fn){
let obj = null;
return function(){
obj ? obj : (obj = fn.apply(this,arguments));
}
};
如何强制释放闭包?
//强制释放闭包的方法,就是给保存闭包的变量,重新赋值为null
function f1(){
var n=999;
return function f2(){
alert(n);
}
}
var a = f1();
//当代码运行到这里,f1虽然已经执行完毕,但是变量n并未被回收,而是被变量a(也就是返回的函数f2)
//所引用着
a=null;
//代码到这里的时候,变量a又指向了null,a和函数f2的联系被切断。f2不再被引用,因此f2被回收
//f2被回收后,它对n的引用也就不存在了,因此同理,n不再被引用,n也被回收不再占用内存。
11、call 和apply的区别是什么?
共同点
call(对象,param1,param2,...)和apply(对象,[param1,param2,...])的共同点:都是用来改变函数体内this的指向,函数的第一个参数代表函数体内的this的指向
如果参数为空或者为null,则this对象的作用域是全局作用域,在浏览器中则指向window对象。但在严格模式下,函数体内的this=null
不同点
call(对象,param1,param2,...)和apply(对象,[param1,param2,...])不同点
apply()接受两个参数,第二个参数是一个带下标的集合,类数组或数组;call()传入的参数数量不固定,从第二个参数开始是具体的参数
call/apply的用途
1)改变函数体内部this的指向
2)Function.prototype.bind-----指定函数体内的this指向,即bind()函数的第一个参数就是函数体内this的指向
请解释 Function.prototype.bind
Function.prototype.bind方法会创建一个新函数,当这个新函数被调用时,它的this值是传递给bind()的第一个参数
function f(y, z){
return this.x + y + z;
}
var m = f.bind({x : 1}, 2);//f()中的this指向{x : 1},y=2,z=3
console.log(m(3));//6
//2
function f(y, z){
return this.x + y + z;
}
var m = f.bind({x : 1}, 2,3);//f()中的this指向{x : 1},y=2,z=3
console.log(m()); //6
3)借用其他对象的用法
借用构造函数-----实现类似继承的效果,在子函数的内部通过apply/call调用父函数的构造函数
借用其他函数实现一些特定的功能
1、将arguments类数组转成真正的数组arr = [].slice.call(arguments);
2、截去arguments的头一个函数arr = [].shift.call(arguments);
3、往arguments中添加新的元素arr = [].push.call(arguments,2);
4、改变参数Math.min().call(null,1,2,3,4) Math.max.apply(null,[1,2,3,4])
11、caller 和callee的区别是什么?
caller返回一个函数的引用,这个函数调用了当前的函数
callee返回正在执行的函数本身的引用,它是arguments的一个属性
1)caller需要注意
a) 这个属性只有当函数在执行时才有用
b) 如果在javascript程序中,函数是由顶层调用的,则返回null
// 例子1
var a = function() {
alert(a.caller);
}
var b = function() {
a();
}
b();
// 上面的代码中,b调用了a,那么a.caller返回的是b的引用,结果如下:
var b = function() {
a();
}
// 例子二
// 如果直接调用a(即a在任何函数中被调用,也就是顶层调用),返回null:
var a = function() {
alert(a.caller);
}
var b = function() {
a();
}
a(); //输出null
2) callee需要注意
a) 这个属性只有在函数执行时才有效
b) 它有一个length属性,可以用来获得形参的个数,因此可以用来比较形参和实参个数是否一致,即比较arguments.length是否等于arguments.callee.length
c) 它可以用来递归匿名函数
var a = function() {
alert(arguments.callee);
}
var b = function() {
a();
}
b();
// a在b中被调用,但是它返回了a本身的引用,结果如下:
var a = function() {
alert(arguments.callee);
}
13、请指出 JavaScript 宿主对象 (host objects) 和原生对象(本地对象) (native objects) 的区别
宿主对象是指DOM和BOM
原生对象是Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、Math等对象
14、请指出以下代码的区别
function Person(){} //声明一个函数Person()
var person = Person() //将函数Person()的结果返回给person,如果没有返回值,则person为undefiend
var person = new Person();//new一个Person的实例对象
9、事件的定义? IE与火狐的事件机制有什么区别? 如何阻止冒泡? 事件冒泡机制
事件的定义:事件就是文档或浏览器窗口中发生的一些特定的交互瞬间
观察员模式的模型:使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码。这种模式支持页面的行为(javascript代码)与页面的外观(HTML CSS代码)之间的松散耦合。
事件流:描述的是从页面中接收事件的顺序,事件流包括3个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。IE的事件流是事件冒泡流,FireFox的事件流是事件捕获流
IE的事件流是事件冒泡流:即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点),然后沿着DOM树逐级向上传播到较为不具体的节点,直到document对象
阻止事件冒泡:使用e.stopPropagation()
FireFox的事件流是事件捕获流:即事件开始时由较为不具体的元素,即document对象,然后沿着DOM树逐级向下传播到最具体的元素。
w3c绑定事件:target.addEventListener(event,handler,false) //false表示在冒泡阶段调用事件处理程序
w3c解绑事件:target.removeEventListener(eventType, handler, false) //true表示在捕获阶段调用事件处理程序
ie绑定事件: target.attachEvent(on+event, handler)
ie解绑事件:target.detachEvent("on"+eventType, handler)
10、什么是 “use strict”; ? 使用它的好处和坏处分别是什么?
"use strict":表示使用严格模式
优点
● 消除Javascript语法的一些不严谨之处,减少一些怪异行为;
● 消除代码运行的一些不安全之处,保证代码运行的安全;
● 提高编译器效率,增加运行速度;
● 为未来新版本的Javascript做好铺垫
缺点
严格模式改变了语义。依赖这些改变可能会导致没有实现严格模式的浏览器中出现问题或者错误
严格模式的限制
1)变量必须声明后再使用
2)在使用默认参数的函数中的参数不能有同名属性,否则报错,在同一作用域中不能有同名参数
3)增加了保留字(比如protected、static和interface)
4)禁止this指向全局对象
5)不能对只读属性赋值,否则报错
6)不能使用前缀0表示八进制数,否则报错(用0O表示)
7)不能删除不可删除的属性,否则报错
8)不能使用with语句(with语句接收的对象会添加到作用域链的前端并在代码执行完之后移除)( with(obj) )
9)eval不会在它的外层作用域引入变量 ( eval(str) )
10 )eval和arguments不能被重新赋值
11)arguments不会自动反映函数参数的变化
12)不能使用arguments.callee
13)不能使用arguments.caller
14)不能使用fn.caller和fn.arguments获取函数调用的堆栈
15)不能删除变量delete prop,会报错,只能删除属性delete global[prop]
11、匿名函数的作用
定义回调函数 ;立即执行函数;作为返回值的函数;使用方法var foo = function() {}定义的函数
13、如何编写高性能的Javascript
* 第一章:减少javascript对性能的影响:
*
* </body>闭合标签之前,将所有的<script>标签放到页面底部。这能确保在脚本执行前页面已经完成了渲染。不然<script>标签会阻塞页面的渲染。
* 合并脚本。页面中的<script>标签越少,加载也就越快,响应也更迅速。无论外链文件还是内嵌脚本都是如此。因为每一次<script>外部文件都会有一次HTTP请求
* 有多种无阻塞下载javascript的方法
*
* 使用defer属性(不推荐,只有IE支持)
* 使用动态创建的<script>元素来下载并执行代码。
* 使用XHR对象下载javascript代码并注入页面中
* 第二章:数据访问
*
* 对象成员的嵌套也会开销系统资源。location.href永远会比window.location.href快。
* 访问直接量和局部变量的速度最快,相反,访问数组元素和对象成员相对较慢。
* 由于局部变量存在于作用域链的起始位置,因此访问局部变量比访问跨作用域变量更快。变量在作用域链中的位置越深,访问所需时间就越长。由于全局变量总处在作用域链的最末端,因此访问速度也是最慢的。
* 避免使用with语句,因为它会改变运行期上下文作用域链。同样,try-catch语句中的catch子句也有同样的影响,因此要小心使用。
* 嵌套的对象成员会明显影响性能,尽量少用
* 属性或方法在原型链中的位置越深,访问它的速度也越慢。
* 通常来说,你可以通过把常用的对象成员、数组元素、跨域变量保存在局部变量中来改善javascript的性能,因为局部变量访问速度最快。
* 第三章:DOM编程
*
* 最小化DOM访问次数,尽可能在javascript端处理
* 如果需要多次访问某个DOM节点,请使用局部变量存储它的引用
* 小心处理HTML集合,因为它实时联系着底层文档。把集合的长度缓存到一个变量中,并在迭代中使用它。如果需要经常操作集合,建议把它拷贝到一个数组中。
* 如果可能的话,使用速度更快的API,比如querySelectorAll()和firstElemengChild()
* 要留意重绘和重排;批量修改样式时,“离线”操作DOM树,使用缓存,并减少访问布局信息的次数。
* 动画中使用绝对定位,使用拖放代理
* 使用事件委托来减少事件处理器的数量
* 第四章:算法和流程控制
*
* for、while和do-while循环性能相似,所以没有一种循环类型明显快于或慢于其他类型
* 避免使用for-in循环,除非你需要遍历一个属性数量未知的对象
* 改善循环性能的最佳方式是减少每次迭代的运算量和减少循环迭代次数
* 通常来说,switch总是比if-else快,但并不总是最佳解决方案
* 在判断条件较多时,使用查找表比if-else和switch更快
* 浏览器的调用栈大小限制了递归算法在javascript中的应用;栈溢出错误会导致其他代码中断运行
* 如果你遇到栈溢出错误,可将方法改为迭代算法,或使用Memoization来避免重复计算。
* 第五章:字符串和正则表达式
*
* 当连接数量巨大或尺寸巨大的字符串时,数组项连接(就是用concat()等连接字符串的方法)是唯一在IE7及更早版本中性能合理的方法
* 如果不考虑IE7及更早版本的性能,数组项连接是最慢的字符串连接方法之一。推荐使用简单的+和+=操作符替代,避免不必要的中间字符串
* 回溯既是正则表达式匹配功能的基本组成部分,也是正则表达式的低效之源
* 回溯失控发生在正则表达式本应该快速匹配的地方,但因为某些特殊的字符串匹配动作导致运行缓慢甚至浏览器崩溃。避免这个问题的办法是:使相邻的字元互斥,避免嵌套量词对同一字符串的相同部分多次匹配,通过重复利用向前查看的原子组去除不必要的回溯
* 提高正则表达式效率的各种技术手段会有助于正则表达式更快地匹配,并在非匹配位置上花更少的时间
* 正则表达式并不总是完成工作的最佳工具,尤其当你只搜索字面字符串的时候
* 尽管有许多方法可以去除字符串的首尾空白,但使用两个简单的正则表达式(一个用来去除头部空白,另一个用于去除尾部空白)来处理大量字符串内容能提供一个简洁而跨浏览器的方法。从字符串末尾开始循环向前搜索第一个非空白字符,或者将此技术同正则表达式结合起来,会提供一个更好的替代方案,它很少受到字符串长度的影响
* 第六章:快速响应的用户界面
*
* 任何javascript任务都不应当执行超过100毫秒。过长的运行时间会导致UI更新出现明显的延迟,从而对用户体验产生负面的影响。
* javascript运行期间,浏览器响应用户交互的行为存在差异。无论如何,javascript长时间运行将导致用户体验变得混乱和脱节
* 定时器可用来安排代码延迟执行,它使得你可以把长时间运行的脚本分解成一系列的小任务
* Web workers是新版浏览器支持的特性,它允许你在UI线程外部执行javascript代码,从而避免锁定UI
* 第七章:Ajax
*
* 作为数据格式,纯文本和HTML只适用于特定场合,但它们可以节省客户端的CPU周期。XML被广泛应用而且支持良好,但是它十分笨重且解析缓慢。JSON是轻量级的,解析速度快(被视为原生代码而不是字符串),通用性与XML相当。字符分隔的自定义格式十分轻量,在解析大量数据集时非常快,但需要编写额外的服务端构造程序,并在客户端解析。
* 当从页面当前所处的域下请求数据时,XHR提供了最完善的控制和灵活性,尽管它会把接收到的所有数据当成一个字符串,且这有可能降低解析速度。另一方面,动态脚本注入允许跨域请求和本地执行javascript和JSON但是它的接口不那么安全,而且还不能读取头信息或相应代码。Multipart XHR可以用来减少请求数,并处理一个响应中的各种文件类型,但是它不能缓存接收到的响应。当需要发送数据时,图片信标是一种简单而有效的方法。XHR还可以用POST方法发送大量数据。
* 除了这些格式和传输技术,还有一些准则有助于加速你的Ajax
*
* 减少请求数,可通过合并javascript和CSS文件,或使用MXHR
* 缩短页面的加载时间,页面主要内容加载完成后,用Ajax获取那些次要的文件
* 确保你的代码错误不会输出给用户,并在服务端处理错误
* 指导何时使用成熟的Ajax类库,以及何时编写自己的底层Ajax代码
* 第八章:编程实践
*
* 通过避免使用eval()和Function()构造器来避免双重求值带来的性能消耗。同样的,给setTimeout()和setInterval()传递函数而不是字符串作为参数
* 尽量使用直接量创建对象和数组。直接量的创建和初始化都比非直接量形式要快
* 避免做重复的工作。当需要检测浏览器时,可使用延迟加载或条件预加载
* 在进行数学计算时,考虑使用直接操作数字的二进制形式的位运算
* javascript的原生方法总会比你写的任何代码都要快。尽量使用原生的方法
* 第九章:构建并部署高性能javascript应用
*
* 合并javascript文件以减少HTTP请求数
* 使用YUN Compressor压缩javascript文件
* 在服务器端压缩javascript文件(Gzip编码)
* 通过正确设置HTTP响应头来缓存javascript文件,通过向文件名增加时间戳来避免缓存问题
* 使用CDN提供javascript文件,CDN不仅可以提升性能,它也为你管理文件的压缩与缓存
* 第十章:工具(其实这章节不用太认真,因为这本书的截稿时期是2010年,那么长时间总会有新的工具诞生、老的工具湮灭。关注最新工具就好了)
*
* 使用网络分析工具找出加载脚本和页面中其他资源的瓶颈,这回帮助你决定哪些脚本需要加载延迟,或者需要进一步分析
* 尽管传统的经验告诉我们要尽量减少HTTP请求数,但把脚本尽可能延迟加载可以加快页面渲染速度,给用户带来更好的体验
* 使用性能分析工具照吃脚本运行过程中速度慢得地方,检查每个函数所消耗的时间,以及函数被调用次数,通过调用栈自身提供的一些线索来找出需要集中精力优化的地方
* 尽管耗费的时间和条用次数通常是数据中最有价值的部分,但仔细观察函数的调用过程,你也许会发现其他优化目标
遇到了什么比较深刻的技术问题
15、你遇到过比较难的技术问题是什么?你是如何解决的?
技术问题只要花时间去研究一般都会有解决的方法,要是没有攻克之前或者是第一次接触到的话,就会认为它是一个比较难的技术问题,攻克之后就会觉得该问题是小case了。那么当我们在遇到一个自己从没有遇到过的技术问题的时候,肯定会觉得比较难,那么首先会 去google或者查看大牛写的博客,或者去stackoverflow查看等 ,这些网站一般都会有相关的资源。举个例子说,刚开始的时候在业务逻辑中要实现一个功能:前端页面要实时获取服务端发送回来的数据,那么实现这一功能有很多的方法。如果选择ajax的长轮询的话,那就要了解其含义和原理。刚开始接触的时候对其是一头雾水,那么自己就上网找资料去理解去原理,然后用代码实现。在攻克这一问题之后,其实ajax的长轮询很简单。那么在平时的时候,当我们在攻克了技术问题之后,我们可以将其记录下来,或者和别人一起分享,这样我们才能成长的更快。
16、常使用的库有哪些?常用的前端开发工具?开发过什么应用或组件?
常使用的js库:jQuery Bootstrap.js vue.js
常用的前端开发工具:webstorm sublime vs code
开发过的应用:欢乐上墙、微信小程序“信息加密”
17、页面重构怎么操作?
页面重构是一种思想,是页面的二次构造(在实现层次):包括设计稿的重构、过时页面的重构、功能不全页面的重构、代码重构
设计稿的重构:设计师的设计稿可能不是特别符合页面效果,当拿到设计稿时需要通过二次重构和修改达到预期效果
功能不全页面的重构:页面功能不符合用户体验、用户交互
过时页面的重构:使用的是过时的代码和标签,跟不上时代的发展
代码重构:代码质量、CSS优化、更好的语义化、浏览器兼容、页面性能、SEO优化
页面重构需要注意以下几点
1)删除无用代码,精简代码:删除无用的css和javascript
2)前端代码规范
(1)把页面中内联样式及头部样式提取到单独的css文件中
(2)调整代码的缩进
(3)更改已经不支持的标签如:<b>
(4)在javascript中减少全局变量的使用,缩小变量的作用域
(5)整理基础类库,减少因为版本的问题造成多个文件
3)前端代码模块化
(1)按模块归类css代码与js代码,放到模块对应的文件夹中
(2)按模块分离js代码,定义不同的命名空间
4)提高页面加载性能
(1)首屏加载技术:将不影响首页展现的javascript文件延迟到页面加载后加载
(2)javascript改为按需加载
(3)图片的懒加载、图片使用雪碧图
(4)调整css和js的引用顺序
(5)给静态文件设置缓存
(6)加载CDN上的文件
21、json的了解及用法,与xml有何不同?
1)定义
json的定义(javascript object notation)
一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当今大部分语言的支持),从而可以在不同平台间进行数据交换。JSON采用兼容性很高的文本格式,同时也具备类似于C语言体系的行为
xml的定义
扩展标记语言 (Extensible Markup Language,XML) ,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 XML是标准通用标记语言 (SGML) 的子集,非常适合 Web 传输。XML 提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据
2)优缺点
JSON优点
(1) 数据格式比较简单, 易于读写, 格式都是压缩的, 占用带宽小
(2)易于解析这种语言, 客户端JavaScript可以简单的通过eval()进行JSON数据的读取
(3) 支持多种语言, 包括ActionScript, C, C#, ColdFusion, Java,JavaScript, Perl, PHP, Python, Ruby等语言服务器端语言, 便于服务器端的解析
(4)在PHP世界, 已经有PHP-JSON和JSON-PHP出现了, 便于PHP序列化后的程序直接调用. PHP服务器端的对象、数组等能够直接生JSON格式, 便于客户端的访问提取.
(5)因为JSON格式能够直接为服务器端代码使用, 大大简化了服务器端和客户端的代码开发量, 但是完成的任务不变, 且易于维护
JSON缺点
(1) 没有XML格式的广泛性和通用性
(2)JSON格式目前在Web Service中推广还属于初级阶段
XML优点
(1)格式统一, 符合标准
(2)容易与其他系统进行远程交互, 数据共享比较方便
XML缺点
(1)XML文件格式文件庞大, 格式复杂, 传输占用带宽
(2)服务器端和客户端都需要花费大量代码来解析XML, 不论服务器端和客户端代码变的异常复杂和不容易维护
(3)客户端不同浏览器之间解析XML的方式不一致, 需要重复编写很多代码
(4)服务器端和客户端解析XML花费资源和时间
3)JSON 和 XML 优缺点的比较
(1)在可读性方面,JSON和XML的数据可读性基本相同。JSON是建议的语法,XML是规范的标签形式
(2) 在可扩展性方面,XML天生有很好的扩展性,JSON当然也有
(3)在编码难度方面,XML有丰富的编码工具,比如Dom4j、JDom等,JSON也有json.org提供的工具,但是JSON的编码明显比XML容易许多,即使不借助工具也能写出JSON的代码,可是要写好XML就不太容易了
(4) 在解码难度方面,XML的解析得考虑子节点父节点,让人头昏眼花,而JSON的解析难度几乎为0
(5)在流行度方面,XML已经被业界广泛的使用,而JSON才刚刚开始,但是在Ajax这个特定的领域,未来的发展一定是XML让位于JSON
(6) JSON和XML同样拥有丰富的解析手段
(7) JSON相对于XML来讲,数据的体积小
(8)JSON与JavaScript的交互更加方便
(9)JSON对数据的描述性比XML较差
(10)JSON的速度要远远快于XML
24、juery中如何将数组转化为json字符串,然后在转化回来
现代浏览器中提供了JSON.stringify()方法把一个对象转换成json字符串,JSON.parse() 把一个json字符串解析成对象。数组是特殊的对象
//在选择器函数对象上面扩展方法
//将数组扩展成JSON字符串
$.fn.stringifyArray = function(array) {
return JSON.stringify(array)
}
//将JSON字符串解析成数组
$.fn.parseArray = function(array) {
return JSON.parse(array)
}
//调用形式
var array = ['a','b','c'];
var a1 = $("").stringifyArray(array);
var a2 = $("").parseArray(a1);
console.log(typeof a1); //string
console.log(a1); //'["a","b","c"]'
console.log(typeof a2); //object
console.log(a2); //["a","b","c"]
29、函数几种定义方法
1)函数声明:即使用 function 关键字显式定义函,如:
function f(x){
return x+1;
}
2)函数表达式:也称为“函数直接量”,形如:
var f = function(x){return x+1;};
3)使用 Function() 构造函数定义,形如:
Var f = new Function(“x”,”return x+1;”);
30、JavaScript的typeof 返回哪些数据类型----返回值都是字符串的形式---有7种类型,使用Object.prototype.toString.call()方法可以得到所有的数据类型
'number'
'string'
'boolean'
'undefined'
'object'---包括Object/Array/Null
'function'
'symbol'
31、写一个获取非行间样式的函数
在javascript中获取样式一般用的是nodeObj.style.attr这个属性的,但只能获取行间样式,非行间样式比如写在样式表中的样式那么用nodeObj.style.attr获取就会报错,nodeObj.style返回值是undefined
// obj---DOM对象,第二个参数可以是false/null(null----IE9+会有问题),方括号中的是要获取的属性
// 返回值是一个对象
//window可以省略
window.getComputedStyle(obj,false)['attr']
//例子
var style=getComputedStyle(div,false)['width']; alert(style);
// IE8以下不支持上述方法,使用currentStyle
// 返回一个对象
nodeObj.currentStyle['attr']
var style=div.currentStyle['width']; alert(style);
// 我们把它们封装为一个函数,注意了在全局下定义的函数的都会默认设置为window对象的方法,
// 所以以后我们调用方法的时候就可以直接用然后省略前面的对象,
// obj---DOM对象,attr----要获取的样式属性
function getAttr(obj,attr){
var style;
//IE8以下
if(obj.currentStyle){ //当有这个属性的时候currentStyle
style = obj.currentStyle[attr]; //兼容IE 我测试的是ie5+
}
else{
//主流浏览器
style = getComputedStyle(obj,false)[attr];
}
return style;
}
33、jquery 中选择器?---基本上有9种
大概归纳为9种
(1)基本选择器
#id
element
.class
*
selector1,selector2,selectorN
(2)层次选择器:
ancestor descendant
parent > child
prev + next
prev ~ siblings
(3)基本过滤器选择器
:first
:last
:not
:even
:odd
:eq
:gt
:lt
:header
:animated
(4)内容过滤器选择器
:contains
:empty
:has
:parent
(5)可见性过滤器选择器
:hidden
:visible
(6)属性过滤器选择器
[attribute]
[attribute=value]
[attribute!=value]
[attribute^=value]
[attribute$=value]
[attribute*=value]
[attrSel1][attrSel2][attrSelN]
(7)子元素过滤器选择器
:nth-child(n)
:first-child
:last-child
:only-child
(8)表单选择器
:input
:text
:password
:radio
:checkbox
:submit
:image
:reset
:button
:file
:hidden
(9)表单过滤器选择器
:enabled
:disabled
:checked
:selected
js中几种选择器-----同上
HTML5出现前js查找DOM元素有3种原生的方法
document.getElementById('id')
document.getElementsByClassName('className')
document.getElementsByTagName('div')
H5新增2种方法
querySelector()---据选择器规则返回第一个符合要求的元素
querySelctorAll()---根据选择器规则返回所有符合要求的元素
38、JavaScript的本地对象,内置对象和宿主对象
1)本地对象----ECMA-262 把本地对象(native object)定义为“独立于宿主环境的 ECMAScript 实现提供的对象”。简单来说,本地对象就是 ECMA-262 定义的类(引用类型)
Object
Function
Array
String
Boolean
Number
Date
RegExp
Error
EvalError
RangeError
ReferenceError
SyntaxError
TypeError
URIError
2)内置对象---ECMA-262 把内置对象(built-in object)定义为“由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现”,ECMA-262 只定义了两个内置对象,即 Global 和 Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)
3)宿主对象-----所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境提供的对象。
所有 BOM 和 DOM 对象都是宿主对象
39、js编写一个数组去重的方法
使用ES6的Set()和展开运算符,这种写法简单,但是复杂度很高
var arr = [1,1,2,2];
var arr2 = new Set(arr);
console.log([...arr2]);
先对Array的indexOf()和forEach()进行polyfill
Array.prototype.indexOf = Array.prototype.indexOf || function(item) {
for (var i = 0, j = this.length; i < j; i++) {
if (this[i] === item) {
return i;
}
}
return -1;
}
Array.prototype.forEach = Array.prototype.forEach || function(callback, thisArg) {
if (!callback || typeof callback !== 'function') return;
for (var i = 0, j = this.length; i < j; i++) {
callback.call(thisArg, this[i], i, this);
}
}
1)遍历数组,建立新数组,利用indexOf判断是否存在于新数组中,不存在则push到新数组,最后返回新数组
function removeDuplicatedItem(ar) {
var ret = [];
for (var i = 0, j = ar.length; i < j; i++) {
if (ret.indexOf(ar[i]) === -1) {
ret.push(ar[i]);
}
}
return ret;
}
2)遍历数组,利用object对象保存数组值,判断数组值是否已经保存在object中,未保存则push到新数组并用object[arrayItem]=1的方式记录保存
function removeDuplicatedItem2(ar) {
var tmp = {},
ret = [];
for (var i = 0, j = ar.length; i < j; i++) {
if (!tmp[ar[i]]) {
tmp[ar[i]] = 1;
ret.push(ar[i]);
}
}
return ret;
}
3)数组下标判断法, 遍历数组,利用indexOf判断元素的值是否与当前索引相等,如相等则加入
function removeDuplicatedItem3(ar) {
var ret = [];
ar.forEach(function(e, i, ar) {
if (ar.indexOf(e) === i) {
ret.push(e);
}
});
return ret;
}
4)以上方法的性能效率都不是很高,可以对数组进行遍历,然后将元素作为对象的属性保存,因为每次都是赋值操作,所以性能高很多
function deleteRepeatEle(arr){
let obj = {};
let result = [];
for(let val of arr){
obj[val] = val; //数组元素去重,那么对于对象相同的key都会覆盖,达到去重的目的
}
for(let key in obj){
result.push(~~key); //注意这里的对象的key都是字符串类型,所以需要转换成整数
}
return result;
}
let arr = [1,1,2,3,3,3];
console.log( deleteRepeatEle(arr) );
40、split()join()的 区别 数组方法pop()push()unshift()shift()
数组和字符串之间的转换方式
1)将字符串切割成字符串数组---stringObject.split(separator,howmany)
seperator-----字符串、正则表达式,必需
howmany------指定返回的数组的最大长度
2)把数组中的所有元素放入一个字符串-----arrayObject.join(separator)
seperator-----可选,指定要使用的分隔符,如果省略该参数,则使用逗号作为分隔符
数组方法
1)pop()----删除 arrayObject 的最后一个元素,把数组长度减 1,并且返回它删除的元素的值。如果数组已经为空,则 pop() 不改变数组,并返回 undefined 值
arrayObject.pop()
2)push()-----把它的参数顺序添加到 arrayObject 的尾部,返回数组的新长度,它直接修改 arrayObject,而不是创建一个新的数组
arrayObject.push(newelement1,newelement2,....,newelementX)
3)shift()---把数组的第一个元素从其中删除,并返回第一个元素的值,数组是空的,那么 shift() 方法将不进行任何操作,返回 undefined 值,改变原数组
arrayObject.Shift()
4)unshift()----向数组的开头添加一个或更多元素,并返回新的长度,改变原数组
arrayObject.unshift(newelement1,newelement2,....,newelementX)
89、介绍js基本数据类型
1)ECMAScript中有5种基本数据类型: Undefined、Null、Boolean、Number和String
1种复杂的数据类型—Object,Object本质上是由一组无序的名值对组成的
其中Undefined、Null、Boolean、Number都属于基本类型。Object、Array和Function则属于引用类型,String有些特殊
2)变量
ES6前声明变量只有:var function
ES6声明变量有:const---声明基本类型的常量变量值不能修改,引用类型的常量内部值可以修改只是保证地址一样就可以了 let Symbol
3)typeof操作---可以检测基本数据类型,返回字符串
var a
console.log(typeof a); //'undefined'
a = 1
console.log(typeof a); //'number'
a = 'aa'
console.log(typeof a); //'string'
a = true
console.log(typeof a); //'boolean'
a = null
console.log(typeof a); //'object'
a = []
console.log(typeof a); //'object'
a = {}
console.log(typeof a); //'object'
Boolean()函数的转换
42、ECMAScript和JavaScript的区别?
1)ECMAScript是一个标准,然后被广泛应用于浏览器的客户端脚本,它的表现形式有JavaScript、JScript和ActionScript
ECMAScript标准是不断的更新发展,比如在ES6中就有:类、模块、块级作用域(let const)、箭头函数等
2)Javacript 是ECMAScript 实现的一种脚本语言,运行在浏览器端的脚本语言,一般是用来做客户端页面的交互,一个完整的JavaScript由3个不同的部分组成:ECMAScript DOM BOM
javascript的运行环境
javascript的运行环境是运行在浏览器内核中的JS引擎
js只可以运行在浏览器中吗?
不是
js能运行在浏览器中,是因为浏览器为其提供了Js引擎
js能运行在哪,取决于这个环境有没有特定的平台(有没有提供JS引擎)
浏览器的作用:
1)作请求和响应的(打开浏览器,在地址栏输入地址,输入enter):请求一个HTTP地址(封装一个请求报文),浏览器最大的一个作用就是将一个URL地址封装成一个请求报文
2)解析服务器返回来的响应报文(内容有可能不一样)
渲染引擎的作用:html => 渲染HTML css => 渲染css image => 渲染image
js引擎的作用: js => 执行(解析)js
运行在浏览器中的js能做什么?
1)操作DOM(对DOM增删改、注册事件)
2)AJAX操作/跨域
3)BOM( 页面跳转、历史记录、console.log() 、alert() )
4)ECMAScript
运行在浏览器中的js不能做什么?
1)不能对文件操作(文件的增删改查CDUR)
2)没有办法操作系统
3)没有办法操作网络
为什么没有上面的功能
由于js运行环境特殊(js运行在浏览器的js引擎中,写的代码是在不认识的人的浏览器中运行),比如说在服务器上写了一个js文件,由于js并不是在服务器上执行,而是在客户端执行,客户端在一开始的时候发送了一个index.js的请求,请求完之后拿到这个文件,在浏览器的执行引擎里面执行indx.js,相当于他要是有这个功能的话,这个js文件,只要是客户端请求了,就能操作操作系统的信息,读取本地文件这是有问题的。对于用户访问一个网页是无感知的,它不知道网页背后做了什么,万一Js读了一个文件,把文件信息发出去了,对这个文件而言,就是一个隐形的暴露。所以说最早的时候,不是不能做,由于运行环境比较特殊,没法做
在开发人员能力相同的情况下,编程语言的能力取决于什么?
1)语言本身只是提供定义变量,定义函数,定义类型、流程控制、循环结构之类的操作
2)编程语言的能力取决于运行该语言的平台(环境)
3)对于js而言,我们常说的Js实际是ECMAScript,大部分能力都是由浏览器的执行引擎决定的
4)BOM和DOM是浏览器开放出来的API
Cordova平台提供JS调用摄像头,操作本地文件的API
JAVA:1)java既是语言也是平台 2)java运行在java虚拟机上面,java虚拟机可以跨平台,所以不需要运行在多个平台上(跑java代码的)(唯一的运行平台)
C#语言:平台 .net framework,也可以运行在MONO平台上(有人需要将C#运行在Linux平台上,所以出现了MONO)
PHP:既是语言也是平台,只能运行在PHP平台上(PHP可以跨操作系统)
36、Node.js的适用场景
Node的定义
● Node.js是一个基于Chrome V8引擎(进行代码解释)的Javascript运行环境(平台),不是一门语言,也不是js框架
● Node.js使用了一个事件驱动、非阻塞I/O模型(异步操作),使其轻量又高效
● Node.js的包管理器npm,是全球最大的开源库生态系统
Node中异步操作的理解(非阻塞模型)
1)Node采用chrom v8引擎处理js脚本,V8最大的特点就是单线程运行,一次只能运行一个任务
2)Node大量采用异步操作,即任务不是马上执行,而是插在任务队列的尾部,等到前面的任务运行完成后在执行
3)异步操作提高代码的相应能力
node内部是多线程的,node有主线程和子线程,将耗时的操作比如文件或者网络等阻塞操作交给系统内部的线程处理,之所以说node是一个单线程的,对于外界编程的API,所有的东西都是一个线程,只有内部的文件操作、网络操作这些阻塞操作才会交给内部线程去做,内部线程的通知回来通过事件机制回来的
举个例子:读系统文件就是一个异步的过程,花的时间不知道是多久,但是读完就会执行回调函数
var fs = require('fs');
//读文件就是一个异步的过程,从系统读文件,不知道用时多久
// 但是系统读完,就会执行后面的回调函数
// 在Node里面默认回调的第一个参数是error
// 如果error = null,则表示没有错误,可以使用content
fs.readFile('ddd',(error,content) => {
if(error){
throw error
}
return content;
});
异步回调的问题(回调黑洞)
回调函数嵌套过深(在一个回调里面再有一个回调,回调里面在嵌套回调,有可能造成死循环)
相比于传统的代码
1)异步事件驱动的代码不容易阅读,不容易调试,不容易维护
js中有哪些操作是异步操作
1)setTimeout()
2)setInterval()
3)$.ajax()
Node中异步操作---Node中所有会发生阻塞的操作都是异步的
1)文件操作
2)网络操作
非阻塞I/O---input/output---从输入到输出之间的转化过程
事件驱动和非阻塞机制(异步处理)
1)Node平台将一个任务,连同该任务的回调函数,放到一个事件循环系统中
2)事件循环高效的管理系统线程池,同时高效执行每一个任务
3)当任务执行完之后,自动执行回调函数
总之:
1)node中将所有的阻塞操作,交给了内部实现的 线程池;
2)Node本身主线程主要就是不断的往返调度
什么是进程?
1)每一个正在运行的应用程序都称为进程
2)每一个应用程序都至少有一个进程
3)进程是用来给应用程序提供一个运行的环境
4)进程是操作系统为应用程序分配资源的一个单位
什么是线程?
1)线程是用来执行应用程序中的代码
2)在一个进程内部,可以有很多线程
3)在一个线程内部,同时只可以做一件事
4)传统的开发方式大部分都是I/O阻塞的
5)所以需要多线程来更好的利用硬件资源
什么原因让多线程没落?
1)多线程都是假的,因为只有一个CPU(单核)(单核,同时只能做一件事)
2)线程之间共享某些数据,同步某个状态都很麻烦
3)更致命的是:
a) 创建线程耗费资源
b) 线程数量有限
c) CPU在不同线程之间转换,有个上下文转换,这个转换非常耗时
非阻塞I/O的优势---Node的核心之一
1)提高代码的响应效率
2)充分利用单核CPU的优势
3)改善I/O的不可预测带来的问题
用白话来解释Node的定义
1)Node就是javascript语言在服务器端的运行环境
所谓运行环境(平台)有两层意思:
首先,js语言通过Node在服务器上运行,在这个意义上,Node有点像js的虚拟机
其次,Node提供大量工具库(API),使得js语言与操作系统互动(读写文件,新建子进程),在这点上Node又是js的工具库
为什么Node使用js语言实现?
1)js目前是开发行业中最火热的一门语言,会的人很多
2)Node.js创始人Ryan Dahl最初希望采用Ruby,但是Ruby的虚拟机效率不行
注意:是Node选择了js,不是js发展出Node
Node.js的实现
1)Node的内部采用Google Chrom的V8引擎,作为js语言解释器
2)通过自行开发的libuv库,调用操作系统资源
My Application----自己写的一个个js文件
Node-----将My Application 中的js文件交给Node平台去运行,js中的if--else while等代码交给Chrom V8引擎执行,因为其能识别执行ES代码,像文件、系统、网络操作等就交给libuv执行
OS----操作系统,操作文件,操作系统,操作网络等
Node.js能够解决的问题: 并发连接问题 I/O阻塞问题
并发连接问题
看下面三个模型:系统线程模型;多线程、线程池模型;异步、事件驱动模型
● 系统线程模型:服务端只有一个线程,并发请求(用户)到达只能处理一个,其余的要先等待,这就是阻塞,正在享受服务的请求会阻塞后面的请求
● 多线程、线程池模型:这个模型已经比上一个有所进步,它调节服务端线程的数量来提高对并发请求的接收和响应,但并发量高的时候,请求仍然需要等待。它有个更严重的问题:服务端与客户端每建立一个连接,都要为这个连接分配一套配套的资源,主要体现为系统内存资源,以PHP为例,维护一个连接可能需要20M的内存。这就是为什么一般并发量一大,就需要多开服务器的原因。
● 异步、事件驱动模型:(Node.js)解决并发连接问题的方法:我们同样是要发起请求,等待服务器端响应;但是与银行例子不同的是,这次我们点完餐后拿到了一个号码,拿到号码,我们往往会在位置上等待,而在我们后面的请求会继续得到处理,同样是拿了一个号码然后到一旁等待,接待员能一直进行处理。
等到饭菜做号了,会喊号码,我们拿到了自己的饭菜,进行后续的处理(吃饭)。这个喊号码的动作在NodeJS中叫做回调(Callback),能在事件(烧菜,I/O)处理完成后继续执行后面的逻辑(吃饭),这体现了NodeJS的显著特点,异步机制、事件驱动整个过程没有阻塞新用户的连接(点餐),也不需要维护已经点餐的用户与厨师的连接。
基于这样的机制,理论上陆续有用户请求连接,NodeJS都可以进行响应,因此NodeJS能支持比Java、PHP程序更高的并发量虽然维护事件队列也需要成本,再由于NodeJS是单线程,事件队列越长,得到响应的时间就越长,并发量上去还是会力不从心。
Node.js解决并发请求问题的方法:更改连接到服务器的方式,每个连接发射(emit)一个在NodeJS引擎进程中运行的事件(Event),放进事件队列当中,而不是为每个连接生成一个新的OS线程(并为其分配一些配套内存)。
IO阻塞
考虑这样的业务场景:需要从多个数据源拉取数据,然后进行处理
● 串行获取数据:这是我们一般的解决方案,以PHP为例:假如获取profile和timeline操作各需要1S,那么串行获取就需要2S
● NodeJS非阻塞I/O:发射/监听事件来控制执行过程:NodeJS遇到I/O事件会创建一个线程去执行,然后主线程会继续往下执行的,因此,拿profile的动作触发一个I/O事件,马上就会执行拿timeline的动作,两个动作并行执行,假如各需要1S,那么总的时间也就是1S。它们的I/O操作执行完成后,发射一个事件,profile和timeline,事件代理接收后继续往下执行后面的逻辑,这就是NodeJS非阻塞I/O的特点。
注意:Java、PHP也有办法实现并行请求(子线程),但NodeJS通过回调函数(Callback)和异步机制会做得很自然。
Node.js的优缺点
优点
● 高并发(最重要的优点)
● 适合I/O密集型应用
缺点
● 不适合CPU密集型应用;CPU密集型应用给Node带来的挑战主要是:由于JavaScript单线程的原因,如果有长时间运行的计算(比如大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起;
解决方案:分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O调用的发起;
● 只支持单核CPU,不能充分利用CPU
● 可靠性低,一旦代码某个环节崩溃,整个系统都崩溃
原因:单进程,单线程
解决方案:
● Nnigx反向代理,负载均衡,开多个进程,绑定多个端口;
●开多个进程监听同一个端口,使用cluster模块;
● 开源组件库质量参差不齐,更新快,向下不兼容
● Debug不方便,错误没有stack trace
Node.js使用场景
● RESTful API
这是NodeJS最理想的应用场景,可以处理数万条连接,本身没有太多的逻辑,只需要请求API,组织数据进行返回即可。它本质上只是从某个数据库中查找一些值并将它们组成一个响应。由于响应是少量文本,入站请求也是少量的文本,因此流量不高,一台机器甚至也可以处理最繁忙的公司的API需求。
● 统一Web应用的UI层
目前MVC的架构,在某种意义上来说,Web开发有两个UI层,一个是在浏览器里面我们最终看到的,另一个在server端,负责生成和拼接页面。
不讨论这种架构是好是坏,但是有另外一种实践,面向服务的架构,更好的做前后端的依赖分离。如果所有的关键业务逻辑都封装成REST调用,就意味着在上层只需要考虑如何用这些REST接口构建具体的应用。那些后端程序员们根本不操心具体数据是如何从一个页面传递到另一个页面的,他们也不用管用户数据更新是通过Ajax异步获取的还是通过刷新页面。
● 大量Ajax请求的应用
例如个性化应用,每个用户看到的页面都不一样,缓存失效,需要在页面加载的时候发起Ajax请求,NodeJS能响应大量的并发请求。
总而言之,NodeJS适合运用在高并发、I/O密集、少量业务逻辑的场景。
Node.js常用的功能
1)Console控制台
2)Crypto(加密)---微信端可能用到
3)Debugger(调试器)
4)HTTP
5)Module(模块)
6)path(路径)
7)url(网址)----用来解析请求的地址
8)util(实用工具)---util.inspect()主要是一种调试模式,可以将对象转换成字符串输出
HTTP模块
有两个功能:通过HTTP模块创建server,然后客户端就可以访问了;另一个就是客户端
Node在Web中的用途
1)做网站,即开发web系统:处理用户所有的请求和给用户响应
2)开发服务端应用程序,即分发数据请求,渲染HTML(这是因为Node的并发性和抗压性比传统的平台好很多,对系统的稳定性有一个很好的提高性)
43、koa的迭代
koa的定义-----基于Node.js平台的下一代web开发框架
1)koa由Express原班人马打造,致力于成为一个更小、更富有表现力、更健壮的WEB框架
2)使用koa编写web应用,通过组合不同的generator,可以免除重复繁琐的回调函数的嵌套,并极大的提升错误处理的效率
3)koa不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写web应用变得得心应手
Koa 是运行在 Node.js 中的 web 服务框架,小而美。
Koa2 是 Koa 框架的最新版本,Koa3 还没有正式推出,Koa1 正走在被替换的路上。
Koa2 与 Koa1 的最大不同,在于 Koa1 基于 co 管理 Promise/Generator 中间件,而 Koa2 紧跟最新的 ES 规范,支持到了 Async Function(Koa1 不支持),两者中间件模型表现一致,只是语法底层不同。
Koa2 正在蚕食 Express 的市场份额,最大的原因是 Javascript 的语言特性进化,以及 Chrome V8 引擎的升级,赋予了 Node.js 更大的能力,提升开发者的编程体验,满足开发者灵活定制的场景以及对于性能提升的需求,蚕食也就水到渠成,2018 年开始,Koa2 会超越 Express 成为本年最大普及量的 Node.js 框架。
以上就是 Koa2 的现状,以及它的趋势,站在 2018 年的节点来看,Koa2 的学习大潮已经到来,那么如果要掌握 Koa2,需要去学习它的哪些知识呢,这些知识跟 Node.js 以及语言规范有什么关系,它的内部组成是如何的,运行机制怎样,定制拓展是否困难,以及它的三方库生态如何,应用场景有哪些,跟前端有如何结合等等
86、Nginx的好处?和Node的比较
Nginx的优点:高并发 响应快
共同点:异步、非阻塞I/O; 事件驱动
不同点:
a) Nginx仅适合于做web服务器,用于反向代理或者负载均衡等服务;在处理业务层用的是C编写,性能更高;Nginx背后的业务层编程思路还是同步编程方式,例如PHP
b) NodeJs高性能平台,用于开发web服务系统,web应用程序等,NodeJs在处理业务层用的是JS编写,采用的是异步编程方式
Nginx的定义
1)Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行
2)其特点是占用内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等
3)Nginx代码完全用C语言从头写成,已经移植到许多体系结构和操作系统,包括:Linux、FreeBSD、Solaris、Mac OS X、AIX以及Microsoft Windows。Nginx有自己的函数库,并且除了zlib、PCRE和OpenSSL之外,标准模块只使用系统C库函数。而且,如果不需要或者考虑到潜在的授权冲突,可以不使用这些第三方库
4)结构与扩展:一个主进程和多个工作进程。工作进程是单线程的,且不需要特殊授权即可运行
86、反向代理
反向代理(Reverse Proxy)方式:是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器;并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器
通常的代理服务器,只用于代理内部网络对Internet的连接请求,客户机必须指定代理服务器,并将本来要直接发送到Web服务器上的http请求发送到代理服务器中。当一个代理服务器能够代理外部网络上的主机,访问内部网络时,这种代理服务的方式称为反向代理服务
反向代理服务器的工作原理
反向代理服务器通常有两种模型:作为内容服务器的替身,作为内容服务器集群的负载均衡器。
1)作为内容服务器的替身
2)作为内容服务器集群的负载均衡器
详细讲解的链接:点击打开链接
43、break和continue区别?
1)break用于完全结束一个循环,跳出循环体,开始执行循环之后的代码
2)continue的功能和break有点类似,区别是continue只是中止本次循环,接着开始下一次循环。而break则是完全中止循环
3)return的功能是结束一个方法。 一旦在循环体内执行到一个return语句,return语句将会结束该方法,循环自然也随之结束。与continue和break不同的是,return直接结束整个方法
49、自执行函数两种写法?
function(){alert(1);}();
以上写法是错的,原因是前半段“function(){alert(1);}”被当成了函数声明,而不是一个函数表达式,从而让后面的“();”变得孤立,产生语法错误
结论:函数体必须是函数表达式
1)最前最后加括号:(函数(实参))---好处:提醒阅读代码的人,这段代码是一个整体
(function(){alert(1);}());
2)function外面加括号:(函数)(实参) ----比方法1少了一个代码整体性的好处
(function(){alert(1);})();
50、取10-100之间的一个随机数?取“a-zA-Z”中的4个随机数?
1)取4个随机字母,利用String.fromCharCode(Unicode) ,A-65,26个英文字母,Math.round(num)---四舍五入
function getRanNum(){
result = [];
for(var i=0;i<4;i++){
var ranNum = Math.round(Math.random() * 25); //生成一个0到25的数字
//26个英文字母
//大写字母'A'的ASCII是65,A~Z的ASCII码就是65 + 0~25;然后调用String.fromCharCode()传入ASCII值返回相应的字符并push进数组里
// String.fromCharCode()接受一个指定的 Unicode 值,然后返回一个字符串
result.push(String.fromCharCode(65+ranNum));
}
return result;
}
console.log(getRanNum())
Math.ceil(num)---向上取整 Math.floor(num)---向下取整 Math.round(num)----四舍五入
Math.random()---[0,1)
1)生成n-m,包含n但不包含m的整数
第一步算出 m-n的值,假设等于w;
第二步Math.random()*w;
第三步Math.random()*w+n;
第四步parseInt(Math.random()*w+n, 10)
2)生成n-m,不包含n但包含m的整数
第一步算出 m-n的值,假设等于w;
第二步Math.random()*w;
第三步Math.random()*w+n;
第四步Math.floor(Math.random()*w+n) + 1
或者Math.ceil(Math.random()*w+n)
3)生成n-m,不包含n和m的整数
第一步算出 m-n-2的值,假设等于w;
第二步Math.random()*w;
第三步Math.random()*w+n +1;
第四步Math.round(Math.random()*w+n+1) 或者 Math.ceil(Math.random()*w+n+1)
4)生成n-m,包含n和m的整数
第一步算出 m-n的值,假设等于w;
第二步Math.random()*w;
第三步Math.random()*w+n;
第四步Math.round(Math.random()*w+n) 或者 Math.ceil(Math.random()*w+n)
51、输出当前日期的前一天?(2016-05-17,则输出2016-05-16)
function getYesterday(){
var date = new Date();
date.setTime( date.getTime()-1000*60*60*24);
var strYear = date.getFullYear();
var strDay = date.getDate();
var strMonth = date.getMonth()+1;
if(strMonth<10)
{
strMonth = '0' + strMonth;
}
if(strDay<10)
{
strDay = '0' + strDay;
}
datastr = strYear + '-' + strMonth + '-' + strDay;
return datastr;
}
54、DOM 0级和2级事件的写法、清除方法及优缺点?
DOM 0级事件----主要分为2个
1)在标签内写onclick
2)在js中写onclick=fucntion(){}
有多个的时候,后面的会替换掉前面的定义
<input id="myButton" type="button" value="Press Me" onclick="alert('thanks');" >
2)
document.getElementById("myButton").onclick = function () {
alert('thanks');
DOM 2级事件----addEventListener(event,handler,boolean)、removeEventListener(event,handler,boolean)
addEventListener():可以为元素添加多个事件处理程序,触发时会按照添加顺序依次调用。
removeEventListener():不能移除匿名添加的函数
55、target和currentTarget区别
event.target返回触发事件的元素
event.currentTarget返回绑定事件的元素
<script>
var ul = document.getElementById("ul");
ul.onclick = function(event){ //event.currentTarget----绑定事件的对象元素ul
var tar = event.target; //触发事件的对象元素
var tagName = tar.nodeName.toLowerCase();
console.log("你点击了:"+tagName);
event.preventDefault();
}
</script>
59、两种定时器的写法及清除
js提供了一些原生的方法来实现延时功能,setInterval() setTimeout setImmediate() requestAnimationFrame()
1)window.setTimeout-----在指定的毫秒数到了之后,执行代码段或函数一次
var id = setTimeout(function(){},millisec) //millisec都是必须的
clearTimeout(id)
//或者
function ff(){}
var id = setTimeout(ff,millisec) //code millisec都是必须的
2)window.setInterval----以固定的时间间隔重复调用一个函数或者代码段
var id = setInterval(code,millisec) //code millisec都是必须的
clearInterval(id)
3)window.setImmediate-----把一些需要长时间运行的操作放在一个回调函数里,在浏览器完成后面的其他语句后,就立刻执行这个回调函数,目前只有IE10支持
var immediateID = setImmediate(func, [param1, param2, ...]); //参数传给func
var immediateID = setImmediate(func);
clearImmediate(immediateID)
4)window.requestAnimationFrame: 告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画
var id = window.requestAnimationFrame(callback);
cancelAnimationFrame(id)
60、判断是手机还是PC
利用正则去判断 navigator.useragent 是否含有 Android/webOs/iphone 等字符串,并且利用修饰符 "i" 做了不区分大小写,然后用正则的方法 test 去判断是否满足
window.location.href = /iPhone|iPad|iPod|iOS|Android/i.test(navigator.userAgent) ? "https://www.baidu.com/" : "http://news.baidu.com/";
61、获取数组中的最大值
62、写一个改变this指向的闭包
var myNumber = {
value: 1,
add: function(i){
var helper = function(i){
console.log(this);
this.value += i;
}
helper(i);
}
}
myNumber.add(1);
1)this指向window对象(因为匿名函数的执行具有全局性,所以其this对象指向window)
2)不能实现value加1(每个函数在被调用时都会自动取得两个特殊变量,this和arguments,内部函数在搜索这两个对象时,只会搜索到其活动对象为止,所以不能实现访问外部函数的this对象)
修改方法(3种)
1)保存this
var myNumber={
value:1,
add:function(i){
var that=this;//定义变量that用于保存上层函数的this对象
var helper=function(i){
console.log(that);
that.value+=i;
}
helper(i);
}
}
myNumber.add(1);
2)使用apply/call改变this的指向
var myNumber={
value:1,
add:function(i){
var helper=function(i){
this.value+=i;
}
helper.apply(this,[i]);//使用apply改变helper的this对象指向,使其指向myNumber对象
}
}
myNumber.add(1);
3)使用bind()绑定this的值
var myNumber={
value:1,
add:function(i){
var helper=function(i){
this.value+=i;
}.bind(this,i);//使用bind绑定,和apply相似,只是它返回的是对函数的引用,不会立即执行
helper(i);
}
}
myNumber.add(1);
63、使用正则写一个验证邮箱的表达式
^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,4}$
70、写一个ajax使用get请求数据的全套代码?
<script type="text/javascript">
function load(){
var xmlhttp;
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera,Safari
xmlhttp= new XMLHttpRequest();
}else{// code for IE6, IE5
xmlhttp= new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange= function(){
if (xmlhttp.readyState ==4 && xmlhttp.status == 200) {//获得了请求数据
var expertinfolist = xmlhttp.responseText;
//发送请求数据到myDiv document.getElementById("myDiv").innerHTML=expertinfolist;
}
}
var url="expert_ZFTservlet?expert_name="+"曾攀";
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
</script>
</head>
<body>
<div id="myDiv">
</div>
<button type="button" onclick="load()" >ChangeContent</button>
</body>
72、js中如何对特殊符号进行转码与编码
对字符串的编码解码推荐使用encodeURIComponent()/decodeURIComponnet()
73、在什么情况下你会用ajax请求来代替通常的跳转
当不进行页面的跳转进行页面的刷新的时候
根据后台返回的数据,进行判断是否需要跳转,这种情况需要使用ajax请求
$.ajax({
type:"POST",
url: //你的请求程序页面随便啦
async:false,//同步:意思是当有返回值以后才会进行后面的js程序。
data://请求需要发送的处理数据
success:function(msg){
if (msg) {//根据返回值进行跳转
window.location.href = '你的跳转的目标地址';
}
}
}
79、一个dom节点被点击的时候,我们希望能够执行一个函数,应该怎么做
对该DOM节点设置click事件监听
obj.addEventListener('click',function(){//todo})
80、请写一个javascript函数parseQueryString,他的用途是吧url参数解析为一个对象,如:
var url = "http://www.test.com/index.php?key0=0&key1=1&key2=2"
function parseQueryString(str){
var tempStr = str.slice( str.lastIndexOf('?')+1 );//获取查询字符串
// 可以写成tempStr = str.split('?')[1]
var tempArr = tempStr.split('&'); //分割成数组
var len = tempArr.length;
var index,
val,
temp,
obj = {};
for(var i =0;i<len;i++){
temp = tempArr[i].split('=');//将tempArr中每个元素在分割成数组['key0','0']
index = decodeURIComponent(temp[0]);
val = decodeURIComponent(temp[1]);
obj[index] = val;
}
return obj;
}
var url = "http://www.taobao.com/index.php?key0=0&key1=1&key2=2";
console.log(parseQueryString(url));
function parseQueryString(url){
// url = http://www.test.com/index.php?key0=0&key1=1&key2=2
var reg = /[?&][^?&]+=[^?&]+/g;
// arr = ['?key0=0','&key1=1','&key2=2']
var arr = url.match(reg); //返回数组,数组元素是匹配到的元素,没有匹配到则返回[]
var obj = {}; //作为返回值的
if(arr){
arr.forEach(function(item){
var tempArr = item.substring(1).split('=');
var index = decodeURIComponent(tempArr[0]);
var val = decodeURIComponent(tempArr[1]);
obj[index] = val;
});
}
return obj;
}
82、在javascript中什么是类数组,如何将类数组转化为标准数组;
类数组:无法直接调用数组方法或期望length属性有什么特殊的行为,但仍可以对真正数组遍历方法来遍历它们。典型的是函数的 argument参数,还有像调用getElementsByTagName,document.childNodes之类的,它们都返回 NodeList对象都属于类数组。可以使用Array.prototype.slice.call(伪数组名)将数组转化为真正的Array 对象。
function reWriteArgument(){
//为了使用unshift数组方法,将argument转化为真正的数组,函数由一个属性arguments,用来保存函数的形参
var args = Array.prototype.slice.call(arguments);
args.shift(args[0]);
return args;
};
console.log(reWriteArgument(1,2));