JS常用面试题

一、闭包:

1、函数嵌套函数、内部函数可以引用外部函数的参数和变量。

参数和变量不会被垃圾回收机制所收回

function aaa(a){

var b = 5;

function bbb(){

alert(a); //内部函数引用外部函数的参数

alert(b);//内部函数引用外部函数的变量

}

}

2、好处:

1)希望一个变量长期驻扎在内存当中。

2)避免全局变量的污染

3)私有成员的存在

function aaa(){

var a = 1; //局部,避免在函数外面是全局,影响其他变量

return function (){

a++;

alert(a);

}

}

var b = aaa();

b(); //2

b(); //3

```

function fn1() {

var a = 1;

function fn2() {

//我们是可以访问到fn1中定义a的值的

//alert(a);

alert(a++);

}

fn2();

}

fn1(); // 1

fn1();// 1 //当一个函数被执行的时候,和函数有关的一些变量会被申明(出现在内存中),当这个函数执行完成以后,如果函数中申明的变量没有再被其他地方所调用,则和这个函数有关的数据自动会被销毁

function fn1() {

var a = 1;

function fn2() {

alert(a++); // alert(a); a=a+1;

//alert(++a) // a=a+1; alert(a);

}

return fn2; //返回出去的是一个引用类型的值

}

var f = fn1();// f 和 fn2 指向的同一内存地址,所以fn1中的变量和函数不会被销毁

f(); // 1

f(); // 2

f = 1; // 断开,f 与 fn2 不指向同一内存,fn1销毁其中的变量和函数

f = fn1(); // f 重新和fn2建立关系

f(); // 1

3、用法:

1)代码模块化:

函数声明 转成 函数表达式:(代码模块化,减少全局变量的污染)

第一种用法:函数自执行:

(function (){

alert(1);

})();

第二种用法:return写法:

var aaa = (function(){

var a = 1;

return function(){

a++;

alert(a);

}

})();

aaa(); //2

aaa(); //3

私有成员的存在:

var aaa = (function(){

var a = 1;

function bbb(){

a++;

alert(a);

}

function ccc(){

a++;

alert(a);

}

return {

b:bbb,

c:ccc

}

})();

aaa.b(); //2

aaa.c(); //3

在外面调用不到a,bbb,ccc

2)在循环中直接找到对应元素的索引:

var li = document.getElementsByTagName(‘li‘);

for(var i = 0;i<li.length;i++){

li[i].onclick = function(){

alert(i); // 3,点击哪一个li都是3,当点击的时候for循环已经结束了为3

}

}

闭包写法

var li = document.getElementsByTagName(‘li‘);

for(var i = 0;i<li.length;i++){

(function(){

li[i].onclick = function(){

alert(i); // 点击第一个弹出1,第二个弹出2,第三个弹出3

}

})(i);// i 驻扎在内存中。

}

return写法

for(var i = 0;i<li.length;i++){

li[i].onclick = (function(){

return function(){

alert(i);

}

})(i);// i 驻扎在内存中。

}

````

模拟选项卡:

for (var i=0; i<aInput.length; i++) {

//show(i);

(function(i) { //这里面的包括形式参数在内的所有 i 都和外面的i无关,仅仅是个形参,可以换成任何字母

aInput[i].onclick = function() {

for (var j=0; j<aInput.length; j++) {

aInput[j].style.background = ‘‘;

aP[j].style.display = ‘none‘;

}

aInput[i].style.background = ‘yellow‘;

aP[i].style.display = ‘block‘;

}

})(i);//这个i代表for循环中的i

}

以上写法就相当于,定义一个带参函数show(a),然后调用,并传参:show(i);

4、IE下会引发内存泄漏,内存一直增加占用cpu。

事件中的函数中引用外部对象时:

window.onload = function(){

var div = document.getElementById(‘div‘);

div.onclick = function(){

alert(div.id); // div 是onclock函数外部的div,引发内存泄漏

}

//解决方法1:

window.onunload = function(){

div.onclick = null;//内存释放

}

}

window.onload = function(){

var div = document.getElementById(‘div‘);

//解决方法2代码:

var id = div.id;

div.onclick = function(){

alert(id); // div 是o‘clock函数外部的div,引发内存泄漏

}

//解决方法2代码:

div = null;//让对象为空

}

二、函数声明与函数表达式:

函数声明: function 函数名(){}

函数表达式: var a = function 函数名(可写可不写)() { }

写函数名:命名函数表达式

不写: 匿名函数表达式

function aaa(){} //函数声明

前面有 =是表达式:

var a = function aaa(){} //命名函数表达式

var a = function () {} // 命名函数表达式

括号中的都是表达式:

(function aaa(){}) //表达式

位运算符都是表达式:

~function aaa(){}

+function aaa(){}

-function aaa(){}

区别:

1.函数表达式可以直接后面加括号执行。而函数声明是不可以的。

2.函数声明可以被提前解析出来的。js的预解析。

错误:function aaa(){}();

正确:var a = function aaa(){}(); //直接执行

~function aaa(){}();

(function aaa(){})();

兼容性:

函数表达式写法不要在外部调用函数名

var a = function aaa(){

alert(2);

}

a(); 所有浏览器都兼容

//aaa();//外面找不到,不推荐使用

函数表达式:

alert(fn1); //undefined

//申明一个变量fn1,赋值了一个函数

var fn1 = function() {

alert(1);

}

函数声明:

alert(fn2); //function

function fn2() {

alert(2);

}

三、对象和函数都是引用的关系

var a = 5;

var b = a;

b += 3;

alert(b);//8

alert(a);//5

对象引用:

var a = [1,2,3];

var b = a;//a把内存地址也给了b,a和b共用一个地址

b.push(4);

alert(b);//1,2,3,4

alert(a);//1,2,3,4

var a = [1,2,3];

var b = a;//a把内存地址也给了b,a和b共用一个地址

b = [1,2,3,4];//b在内存当中又重新占了一个地址,与a分离,与之前的b不同,修改b也不会影响到a了。

alert(b);//1,2,3,4

alert(a);//1,2,3

var obj = {

a:10

};

var obj2 = obj;

obj2.a = 20;

alert(obj.a);//20

四、事件委托

事件委托(事件代理):利用冒泡原理

event对象的事件源:不管在哪个事件中,只要你操作的那个元素就是事件源

ie:window.event.srcElement

标准下:event.target

nodeName:找到当前元素的标签名

好处:

1)提高性能

例子:移到li使标签变颜色。移出颜色消失。移到ul身上没反应。

window.onload = function(){

var oUl = document.getElementById(‘ul‘);

var oLi = oUl.getElementsTageName(‘li‘);

/*for(var i = 0; i < oLi.length;i++){

oLi[i].onclick = function (){

alert(123);

}

}*/

//事件委托:

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;

target.style.background = ‘ ‘;

}

}

2)新添加的元素还会有之前的事件

window.onload = function(){

var oUl = document.getElementById(‘ul‘);

var oLi = oUl.getElementsTageName(‘li‘);

var iNow = 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;

target.style.background = ‘ ‘;

}

//点击一次按钮后会在ul中新添加一个li,当不使用事件委托,直接在li上添加鼠标 移入移出事件,那么新添加的li元素就不会 存在之前li的鼠标移入移出事件。如果使 用事件委托,之前的li事件效果也会添加到新添加的li元素身上,原理就是利用了冒 泡。

button.onclick = function(){

iNow++;

var oLi = document.createElement(‘li‘);

oLi.innerHTML = 1111* iNow;

oUl.appendChild(oLi);

}

}

五:排序:

快速排序:

1、找一个处于中间位置的基准点

2、建立两个数组,分别存储左边和右边的数组。比基准小的放前面,比基准大的放后面。

3、利用递归进行下次比较

递归:一个函数进入下一个这个函数又进入,遇到条件返回上一个,一层一层返回到第一个。

1)函数调用函数自身,执行递的动作

2)最后一次判断一个终止条件,可以执行归的动作。

递归求阶乘:

function test(n){

if(n==1){//当到1时,结束递的过程,开始倒着进行归过程

return 1;//一层层返回,最后一次返回到上一次的函数...

}

return n*test(n-1);// 4x3x2

}

alert(test(4));

快排:

function quickSort(arr){

if(arr.length<=1){ //数组为空或者有一个数

return arr;

}

var num = Math.floor(arr.length/2);//获取中间位置的索引

var numValue = arr.splice(num,1);//把中间位置的基准点分离出来

var left = [];

var right = [];

for(var i =0;i<arr.length;i++){

if(arr[i]<numValue){//如果小于基准点,放在left[]中

left.push(arr[i]);

}else{

right.push(arr[i]);

}

}

//递归第二次是根据第一次基准点分成的左右,分别在左边和右边各找个中间的基准点,再次划分left和right...

return quickSort(left).concat([numValue],quickSort(right));//合并,递归

}

alert(quickSort([12,5,37,6,22,40]);)//5,6,12,22,37,40

冒泡排序:

for (var i=0; i<arr.length-1; i++) {

var f = false;

for (var j=0; j<arr.length-1-i; j++) {

var a = arr[j];

var b = arr[j+1];

if (a < b) {

f = true;

arr[j] = b;

arr[j+1] = a;

}

}

if (!f) {

break;

}

alert(arr);

}

六、枚举算法

枚举算法:

从众多的候选答案中用for来做筛选,通过if找出正确的解

七、JS的跨域:

同一个域名下的js文件不存在跨域问题

ajax不能跨域

解决跨域问题:

1)子域和主域都设置document.domain = ‘a.com‘;

2)服务器代理:XMLHttpRequest代理文件

3)script标签:jsonp的形式(单域操作)

jsonp: json + padding(内填充)

在a网站:在a网站调用b网站

<script>

function box(json){

alert(son.name); //leo

}

</script>

//jsonp在调用函数js的下面调用:

<script src="jsonp.js"></script>

在b网站:jsonp.js:

<script>

box({name = ‘leo‘});

</script>

4)location.hash

5)window.name

6)flash

7)html5 postMessage

八、iframe:

1)在主页面操作iframe页面中元素方法:

var oIframe = .....

所有浏览器都支持:

oIframe.contentWindow.document.getElementById(‘div‘)...

或者:

ie 6 7不支持:

oIframe.contentDocument.getElementById(‘div‘)...

2)在iframe页面中操作父级页面元素方法:

window.parent.document.getElementById(‘div‘)...//父层页面

window.top.document.getElementById(‘div‘)...//最顶层页面

ie下iframe的unload事件只能用绑定的形式

九、console:

console.log()

console.warn();//警告提示

console.error();//错误提示

console.group()//分组开始

console.groupEnd();//分组结束

分组开始和结束之间一般添加console.log()进行调试

console.dir();输出对象所有信息

console.dirxml();显示当前元素包含的代码结构

console.assert();如果是假的断言失败,如果是真的断言不提示信息

console.trace();当前函数的执行过程

console.time(‘计时器‘);代码执行时间。括号中写个标题,表示开始。

...代码

console.timeEnd(‘计时器‘);表示时间结束。括号中写的内容与开始时一样

console.profile();测试代码性能,括号中可以为空。

...代码

console.profileEnd();

十、DOM 优化:

1、dom与JavaScript:

尽量减少js操作dom 的次数

innerHTML与dom方法:

如果要创建多个元素li,在for循环前面声明一个变量:var str =" "; 然后在for循环里用str += ‘<li></li>‘;后在for循环后,添加到ul.innerHTML = str;

var ul = document.get ...

var str = " ";

for(var i =0;i<3000;i++){

str += ‘<li></li>‘;

}

ul.innerHTML = str;

2、减少dom操作

1)节点克隆 - cloneNode

var ul = doc...

var li = document.createElement(‘li‘);

ul.innerHTML = ‘li‘;

for(var i = 0;i<3000;i++){

var new = li.cloneNode(true);

ul.appendChild(new);

}

2)访问元素集合 - 尽量用局部变量

var lei = li.length;//优化

for(var i = 0;i<len;i++){

li[i]....

}

3)元素节点 - 尽量用只获取元素的节点方法

childNodes:能获取元素节点、文本节点

children:只能获取元素节点,推荐使用

firstChild:能获取元素节点、文本节点

firstElementChild:只能获取元素节点,推荐使用

4)选择器API

querySelector、querySelectorAll

3、dom与浏览器:

重排:改变页面的内容(改变元素的宽高和位置)

重绘:浏览器显示内容(重排后的显示就是重绘)

改变元素的背景颜色是重绘

1)添加顺序 - 尽量在appendChild前添加操作

2)合并dom操作 - 利用cssText

3)缓存布局信息 - 把操作用变量先保存起来

4)文档碎片 - createDocumentFragment();创建少的话提高不大

var ul = document.get...

var frag = document.createDocumentFragment();

for(var i = 0 ; i <3000; i++){

var li = document.createElement(‘li‘);

frag.appendChild(li);

}

ul.appendChild(frag);

4、dom与事件的关系:

可以用事件委托

5、dom与前端模板:

能更好地对逻辑和视图分离,MVC架构的基础

十一、变量预解析

alert(a);

//在很多的其他语言中,变量需要先申明再使用,但是在js中我们可以先使用再申明(虽然这里的结果不是10,是undefined)

var a = 10;

//在js中,js解析器会对我们的代码做一些初始化分析的工作,其他包含了这么一项内容,解析器会把程序中变量的申明在程序代码执行之前做一个初始化, 解析器会把变量的申明提前处理,上面的先调用,后申明其实也可以是下面这样

变量预解析:

var a; //申明会提前,赋值不会提前

alert(a);

a = 10;//赋值

作用域:

var a = 10;

function fn() { //函数申明

//var a;

alert(a); //函数内部的a

var a = 100; //申明被提前到了当前作用域的最开始

alert(a);

}

fn(); //undefined 100

十二、callee 和 caller

callee 返回正在执行的函数本身的引用,它是arguments的一个属性

1 这个属性只有在函数执行时才有效

2 它有一个length属性,可以用来获得形参的个数,因此可以用来比较形参和实参个数是否一致,即比较arguments.length是否等于arguments. callee.length

3 它可以用来递归匿名函数。

function fn() {

//arguments.callee => 当前函数

alert(arguments.callee);// function fn(){}

}

eg:让setTimeout来模拟setInterval

函数递归方法:

var a = 1;

function interval() {

setTimeout(function() {

document.title = a++;

interval();

}, 100);

}

interval();

利用闭包自执行和callee来递归:

var a = 1;

(function() {

var _arguments = arguments;

setTimeout(function() {

document.title = a++;

_arguments.callee(); //_arguments代表之前定义的父级函数的

}, 100);

})();

caller:返回一个函数的引用,这个函数调用了当前的函数。即调用该函数的函数。

使用这个属性要注意:

1 这个属性只有当函数在执行时才有用

2 如果在javascript程序中,函数是由顶层调用的,则返回null

functionName.caller: functionName是当前正在执行的函数。

function fn1() {

console.log(1);

console.log(fn1.caller);//fn2

}

function fn2() {

console.log(2);

console.log(fn2.caller); //null

fn1();

}

fn2(); //2 -> null -> 1 -> fn2

十三、call 和 apply

call和apply都是能改变函数内部this的指向

function fn1(a, b) {

console.log(this);

console.log(a + b);

}

fn1.call(document, 1, 2);// 第一个参数是this的指向,可以为null,其后的都是该函数的参数

apply 和call基本一致,不同的是参数的传递上

fn1.apply(document, [1, 2]); //第二个参数就是fn1中的arguments

求最大值:

var arr = [1,6,4,8,3,10,2];

//console.log( Math.max(1,6,4,8,3,10,2) ); //arguments => [1,6,4,8,3,10,2]

console.log( Math.max.apply(null, arr) ); //arguments => arr

十四、this

this

* 函数外 : window

* 函数内 :

* 当一个函数被对象调用,则该函数内的this指向调用该函数的对象

* 当一个函数被事件调用,则该函数内的this指向触发该事件的对象

* 通过call、apply去调用一个函数,那么这个函数指向call、apply的第一个参数(如果call、apply的第一个参数是undefined/null,则this指向调用该函数的对象)

十五、优化

减少全局变量:作用域链

只加载可视区内容

减少dom操作:事件委托 、文档碎片

减少请求和质量:合并JS 、压缩JS

能使用正则尽量使用正则

十六、变量的作用域:

1、外层的变量,内层可以找到(全局);内层的变量,外层找不到(局部)。

var a = 10;

function aaa(){

alert(a);

}

function bbb(){

var a = 20;

aaa();

}

bbb(); // 10

2、当var 不加的时候,会自动生成全局的变量(不建议这样写。最好把所有要定义的变量加上var)。

function aaa(){

var a = b = 10; // 拆分为b = 10; var a = 10;

}

aaa();

alert(a); // undefined

alert(b); // 10;

3、变量的查找是在代码逐行解读下就近原则去寻找var定义的变量或者function的参数 ,当就近没找到就到父级去找。

var a = 0;

function aaa(){

alert(a); // undefined;代码逐行解读

var a = 20;

}

aaa();

```

var a = 10;

function aaa(){

a = 20;

alert(a); //20

}

aaa();

```

var a = 0;

function aaa(){

alert(a); //10

a = 20;

}

aaa();

```

var a = 10;

function aaa(){

bbb();

alert(a);//10

function bbb(){

var a = 20;

}

}

4、当参数和局部变量重名的时候,优先级是等同的。

var a = 10;

function aaa(a){

alert(a); //10

var a = 20;

}

aaa(a);

5、基本类型的赋值,只是值的改变;对象之间是引用的关系,在内存中地址是相同的。

var a = 20;

function aaa(a){

a+=3;

}

aaa(a);

alert(a);//10 函数中的a和外部的a不同

···

var a = [1,2,3];

function aaa(a){

a.push(4); // 修改了外部a

}

aaa(a);

alert(a); // 1,2,3,4

···

var a = [1,2,3];

function aaa(a){

a = [1,2,3,4]; //在内存中重新生成了一个a,与外部的a不同

}

aaa(a);

alert(a); // 1,2,3

时间: 2024-10-28 00:50:20

JS常用面试题的相关文章

js常用功能代码

js常用功能代码(持续更新): 1,--折叠与展开 <input id="btnDisplay" type="button" class="baocun2" value="添加" onclick="changeDisplay()" /> <script type="text/javascript"> function changeDisplay() { var h

JS常用字符串处理方法总结

1.indexOf()方法,从前往后查找字符串位置,大小写敏感,从0开始计数.同理,lastIndexOf() 方法从后往前,两个方法对于相同的检索条件输出的结果是一样的 例如: <script type="text/javascript"> var str="Hello World!" document.write(str.indexOf("Hello"))//输出0 document.write(str.indexOf("

js 常用正则表达式表单验证代码

js 常用正则表达式表单验证代码 js 常用正则表达式表单验证代码,以后大家就可以直接使用了. 正则表达式使用详解 简介 简单的说,正则表达式是一种可以用于模式匹配和替换的强有力的工具.其作用如下:测试字符串的某个模式.例如,可以对一个输入字符串进行测试,看在该字符串是否存在一个电话号码模式或一个信用卡号码模式.这称为数据有效性验证.替换文本.可以在文档中使用一个正则表达式来标识特定文字,然后可以全部将其删除,或者替换为别的文字.根据模式匹配从字符串中提取一个子字符串.可以用来在文本或输入字段中

几道js的笔试题

一.今天在群里看见有同学发了几道关于js的笔试题,拿来研究一下,虽然自己看过了高级程序设计,也看了javascript语言精粹,自以为对js还是有一些理解的,但是真正遇到问题的时候,发现并不能融会贯通,掌握的不扎实.借此机会来回顾一下,共同学习~ 二.几道小题(写出以下几道题的弹出框的内容) (1) 1 if(!("a" in window)){ 2 var a=1; 3 } 4 alert(a); 解析:正常的思路:首先需要了解函数声明提升的概念,在执行代码之前,会先读取函数或者变量

JS 常用正则表达式

匹配负整数的正则表达式: -[0-9]*[1-9][0-9]* 匹配整数的正则表达式: -?\\d+ 匹配非负浮点数(正浮点数 + 0)的正则表达式: \\d+(\\.\\d+)? 匹配正浮点数的正则表达式: (([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*)) 匹配非正浮点数(负浮点数 + 0)的正则表达式: ((-\\d+(\\.\\d+)?)|(0+(\\.0+)?)) 匹配负浮点数的

js 常用函数

document.getElementById("email").setAttribute("属性","属性名");//动态添加ID.class等 document.getElementById("email"). remoAttribute("属性","属性名");//动态删除属性.如ID.Class等 js 常用函数,布布扣,bubuko.com

js常用的验证正则表达式

js 正则表达式使用讲解:各种验证语法 intege:"^-?[1-9]//d*$",     //整数 intege1:"^[1-9]//d*$",     //正整数 intege2:"^-[1-9]//d*$",     //负整数 num:"^([+-]?)//d*//.?//d+$",   //数字 num1:"^[1-9]//d*|0$",     //正数(正整数 + 0) num2:&quo

js常用工具类.

一些js的工具类 复制代码 /** * Created by sevennight on 15-1-31. * js常用工具类 */ /** * 方法作用:[格式化时间] * 使用方法 * 示例: * 使用方式一: * var now = new Date(); * var nowStr = now.dateFormat("yyyy-MM-dd hh:mm:ss"); * 使用方式二: * new Date().dateFormat("yyyy年MM月dd日");

大部分人都会做错的经典JS闭包面试题

大部分人都会做错的经典JS闭包面试题 目录 由工作中演变而来的面试题 JS中有几种函数 创建函数的几种方式 三个fun函数的关系是什么? 函数作用域链的问题 到底在调用哪个函数? 后话 由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) { console.log(o) return { fun:function(m){ return fun(m,