JS高级面试题汇总

目录

1.typeof和instanceof的区别?

2、js 使用 typeof 能得到的哪些类型?

3、解释一下什么是回调函数,并提供一个简单的例子?

4、什么是闭包?

5、什么是内存泄漏(必会)

6、哪些操作会造成内存泄漏?

7.JS 内存泄漏的解决方式

8、说说你对原型(prototype)理解

9、介绍下原型链(解决的是继承问题吗)

10、常见的 js 中的继承方法有哪些

11、介绍 this 各种情况

12、数组中的 forEach 和 map 的区别?

13.for in 和 for of 的区别

14、Call 和 apply,bind 的区别

15.New 操作符具体干了什么呢?

16.用 JavaScript 实现冒泡排序。数据为 23、45、18、37、 92、13、24

17、用 js 实现随机选取 10–100 之间的 10 个数字,存入一 个数组并排序

18、已知数组 var stringArray = [“This”,“is”, “Baidu”,“Campus”],Alert 出”This is Baidu Campus”

19、已知有字符串 foo=”get-element-by-id”,写一个 function 将其转化成驼峰 表示法”getElementById”

20、有这样一个 URL: http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e,请写 一段 JS 程序提取 URL 中的各个 GET 参数(参数名和参数个数不确定),将其按 key-value 形式返回到一个 json 结构中,如{a: "1", b: "2", c: "", d: "xxx", e: undefined}

21、var numberArray = [3,6,2,4,1,5]; (考察基础 API)

22.输出今天的日期,以 YYYY-MM-DD 的方式,比如今天是 2014 年 9 月 26 日,则输出 2014-09-26

23.把两个数组合并,并删除第二个元素。

24、写一个 function,清除字符串前后的空格。(兼容所有浏览器)

25、截取字符串 abcdefg 的 efg

26、判断一个字符串中出现次数最多的字符,统计这个次数

27、将数字 12345678 转化成 RMB 形式 如: 12,345,678

28、Split()和 join()的区别?

29、JavaScript 中如何对一个对象进行深度 clone?

30、js 数组去重,能用几种方法实现

31、谈谈你对 Javascript 垃圾回收机制的理解?

32、Class 和普通构造函数有何区别?

33、什么是 js 事件循环 event loop

34、JS 里垃圾回收机制是什么,常用的是哪种,怎么处理的?

35、计算字符串字节数:

36、js 如何处理防抖和节流

37、Eval 是做什么的?

 38、什么是进程、什么是线程、它们之间是什么关系

39、什么是任务队列?

40、栈和队列的区别?

41、栈和堆的区别?


1.typeof和instanceof的区别?

共同点:

  1. instanceof和typeof是两个运算符,在程序设计中用到,常用来判断一个变量是否为空,或者是什么类型的。

instanceof和typeof的区别:

  • typeof
  1. typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型。
  2. 返回值是一个字符串,该字符串说明运算数的类型。
  3. typeof 一般只能返回如下几个结果:number,boolean,string,function,object,undefined。一般可以使用 typeof 来获取一个变量是否存在,如 if(typeof a!="undefined"){alert("ok")},而不要去使用 if(a) 因为如果 a 不存在(未声明)则会出错,对于 Array,Null 等特殊对象使用 typeof 一律返回 object,这正是 typeof 的局限性。
  • instanceof
  1. instance:实例,例子
  2. a instanceof b?alert("true"):alert("false"); //a是b的实例?真:假
  3. instanceof 用于判断一个变量是否某个对象的实例,如 var a=new Array();alert(a instanceof Array); 会返回 true,同时 alert(a instanceof Object) 也会返回 true;这是因为 Array 是 object 的子类。

 

2、js 使用 typeof 能得到的哪些类型?

typeof用于判断数据类型,返回值有6个字符串:string、number、undefined、boolean、object、function

 

3、解释一下什么是回调函数,并提供一个简单的例子?

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应

案例:

$("button").on("click",function(){

         console.log(1)

})

上面的案例就是一个回调函数  回调函数字面意思理解就是回头再调用的函数,至于什么时候去调用无法确定,需要看用户什么时候去触发它

 

4、什么是闭包?

1.函数套着函数: 函数A嵌套着函数B

2.函数B内部用到函数A的局部变量!

结论:变量与函数B构成闭包

 

5、什么是内存泄漏(必会)

内存泄漏指任何对象在您不再拥有或需要它之后仍然存在(比如你创建了一个变量  let a = 10,但是在你之后的代码中都没有用到变量a ,这就是内存泄漏)

6、哪些操作会造成内存泄漏?

1.意外的全局变量引起的内存泄露

function leak(){
  leak="xxx";//leak成为一个全局变量,不会被回收
}

2.闭包引起的内存泄露

function bindEvent(){
  var obj=document.createElement("XXX");
  obj.οnclick=function(){
    //Even if it's a empty function
  }
}

闭包可以维持函数内局部变量,使其得不到释放。 上例定义事件回调时,由于是函数内定义函数,并且内部函数--事件回调的引用外暴了,形成了闭包。

解决之道,将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对dom的引用。


//将事件处理函数定义在外部
function onclickHandler(){
  //do something
}
function bindEvent(){
  var obj=document.createElement("XXX");
  obj.οnclick=onclickHandler;

//在定义事件处理函数的外部函数中,删除对dom的引用
function bindEvent(){
  var obj=document.createElement("XXX");
  obj.οnclick=function(){
    //Even if it's a empty function
  }
  obj=null;

3.没有清理的DOM元素引用


var elements={
    button: document.getElementById("button"),
    image: document.getElementById("image"),
    text: document.getElementById("text")
};
function doStuff(){
    image.src="http://some.url/image";
    button.click():
    console.log(text.innerHTML)
}
function removeButton(){
    document.body.removeChild(document.getElementById('button'))
}

4.被遗忘的定时器或者回调

var someResouce=getData();
setInterval(function(){
    var node=document.getElementById('Node');
    if(node){
        node.innerHTML=JSON.stringify(someResouce)
    }
},1000)

这样的代码很常见, 如果 id 为 Node 的元素从 DOM 中移除, 该定时器仍会存在, 同时, 因为回调函数中包含对 someResource 的引用, 定时器外面的 someResource 也不会被释放

 

7.JS 内存泄漏的解决方式

1)减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;

2)注意程序逻辑,避免“死循环”之类的 ;

3)避免创建过多的对象  原则:不用了的东西要及时归还

4)使用定时函数定时去清理

 

8、说说你对原型(prototype)理解

JavaScript 是一种通过原型实现继承的语言与别的高级语言是有区别的,像 java,C#是通 过类型决定继承关系的,JavaScript 是的动态的弱类型语言,总之可以认为 JavaScript 中所有 都是对象,在 JavaScript 中,原型也是一个对象,通过原型可以实现对象的属性继承,JavaScript 的对象中都包含了一个” prototype”内部属性,这个属性所对应的就是该对象的原型 “prototype”作为对象的内部属性,是不能被直接访问的。所以为了方便查看一个对象的原 型,Firefox 和 Chrome 内核的 JavaScript 引擎中提供了”proto“这个非标准的访问器(ECMA 新标准中引入了标准对象原型访问器”Object.getPrototype(object)”) 原型的主要作用就是为了实现继承与扩展对象
 


 

9、介绍下原型链(解决的是继承问题吗)

当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去 prototype 里找这个属性,这个 prototype 又会有自己的 prototype,于是就这样一直找下去, 也就是我们平时所说的原型链的概念

 

10、常见的 js 中的继承方法有哪些

1.原型链继承

 function Person(name,age){
      this.name = name
      this.age = age
  }
 Person.prototype.sayName = () =>{
     console.log(this.name)
 }
 function Man(name){
     
 }
 Man.prototype = new Person()
 Man.prototype.name = 'zhangsan'
 var zhangsan = new Man('zhangsan')
 console.log(zhangsan.name) //zhangsan

2.构造函数继承(经典继承)

function Perent(name,age,sex){
    this.name = name
    this.age = age
    this.sex = sex
    this.sayName = function(){
        console.log(this.name)
    }
}

function Child(name,age,sex){
    Perent.call(this,name,age,sex)
}
let child = new Child('lisi' , 18, '男')
console.log(child)   //Child { name: 'lisi', age: 18, sex: '男', sayName: [Function] }

3.组合方式继承(构造函数 + 原型链)

function Animal(name,age){
    this.name = name
    this.age = age
}
Animal.prototype.sayName = function () {
    console.log(this.name)
}
function Cat(name,age,color){
    Animal.call(this,name,age)
    this.color = color
}
Cat.prototype = Animal.prototype  //将Cat的原型指向Animal的原型
Cat.prototype.constructor = Cat   //将Cat的构造函数指向Cat
let cat = new Cat('xiaobai',3,'white')
console.log(cat) //Cat { name: 'xiaobai', age: 3, color: 'white' }
cat.sayName()   //xiaobai

4.es6方法继承

class Per {
    constructor(name){
        this.name = name
    }
    sayName(){
        console.log(this.name)
    }
}

class Son extends Per{
    constructor(name,age){
        super(name)
        this.age = age
    }
}
let son = new Son('zhangsan',18)
console.log(son) //Son { name: 'zhangsan', age: 18 }
son.sayName() //zhangsan

 

11、介绍 this 各种情况

1、以函数形式调用时,this 永远都是 window
2、以方法的形式调用时,this 是调用方法的对象
3、以构造函数的形式调用时,this 是新创建的那个对象
4、使用 call 和 apply 调用时,this 是指定的那个对象
5、箭头函数:箭头函数的 this 看外层是否有函数 如果有,外层函数的 this 就是内部箭头函数的 this 如果没有,就是 window
6、特殊情况:通常意义上 this 指针指向为最后调用它的对象。这里需要注意的一点就是
如果返回值是一个对象,那么 this 指向的就是那个返回的对象,如果返回值不是一个对象那么 this还是指向函数的实例

 

12、数组中的 forEach 和 map 的区别?

相同点 
都是循环遍历数组中的每一项 forEach 和 map 方法里每次执行匿名函数都支持 3 个参数,参数分别是 item(当前每一项), index(索引值),arr(原数组) 匿名函数中的 this 都是指向 window 只能遍历数组 都不会改变原数组 
区别 
map 方法 
1.map 方法返回一个新的数组,数组中的元素为原始数组调用函数处理后的值 
2.map 方法不会对空数组进行检测,map 方法不会改变原始数组。 
3.浏览器支持:chrome、Safari1.5+、opera 都支持,IE9+, 若 arr 为空数组,则 map 方法返 回的也是一个空数组。 
forEach 方法 
1.forEach 方法用来调用数组的每个元素,将元素传给回调函数 
2.forEach 对于空数组是不会调用回调函数的。 无论 arr 是不是空数组,forEach 返回的都是 undefined。这个方法只是将数组中的每一项作为 callback 的参数执行一次

13.for in 和 for of 的区别

for...in 循环主要是为了遍历对象而生,不适用于遍历数组

for...of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象

 

14、Call 和 apply,bind 的区别

共同点: 都可以实现函数内部this被我们指定

不同点: call/apply 会让函数执行的

call后面参数是依次逗号分隔传入

apply:一个参数数组,数组成员是依次传入

bind 不会执行前面的函数!返回值:新的函数 和原来那个函数长的一样,内部this已经被我们固定

15.New 操作符具体干了什么呢?

1.创建了一个空对象

2.把this后面的键值对放到了空对象中

3.把空对象输出

 

16.用 JavaScript 实现冒泡排序。数据为 23、45、18、37、 92、13、24

//升序算法
function sort(arr){
    for (var i = 0; i <arr.length; i++) {
        for (var j = 0; j <arr.length-i; j++) {
            if(arr[j]>arr[j+1]){
                var c=arr[j];//交换两个变量的位置
                arr[j]=arr[j+1];
                arr[j+1]=c;
            }
        };
    };
    return arr.toString();
}
console.log(sort([23,45,18,37,92,13,24]));

 

17、用 js 实现随机选取 10–100 之间的 10 个数字,存入一 个数组并排序

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>

    <body>
        <script type="text/javascript">
            //首先创建一个空数组,用来放10个数字
            var Array = [];
            //接下来定义一个函数,这个函数是写10~100的随机数,我们现在把他封装成一个函数
            function getRandom(num1,num2){
                var transition = num2 - num1 + 1;//这里面的加1是为了能够取到100
                var res = Math.floor(Math.random() * transition + num1);
                return res;
            }
            //上面的代码已经获取了num1~num2的随机数
            //下面是遍历出10个随机数,并把十个数用push放法放到新定义的数组里面
            for(var i = 0; i < 10; i++){
                Array.push(getRandom(10,100));
            }
            //最后用sort方法进行排序
            Array.sort(function(a,b){
                return a > b;
            })
            //打印数组Array
            console.log(Array);

        </script>
    </body>

</html>

 

18、已知数组 var stringArray = [“This”,“is”, “Baidu”,“Campus”],Alert 出”This is Baidu Campus”

var stringArray = ["This", "is", "Baidu", "Campus"] 
alert(stringArray.join(""))

 

19、已知有字符串 foo=”get-element-by-id”,写一个 function 将其转化成驼峰 表示法”getElementById”

var foo = "get-element-by-id"
        var arr = foo.split("-");
        console.log(arr); //["get", "element", "by", "id"]
        for (var i = 1; i < arr.length; i++) {
            arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].slice(1);
        }
        console.log(arr) //["get", "Element", "By", "Id"]
        var arr = arr.join("") //getElementById
        console.log(arr);

解释:

var arr = foo.split("-")  split是将字符串分割为字符串数组

for (var i = 1; i < arr.length; i++) {
    arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].slice(1);
}
注意:这里i是从1开始,因为["get", "element", "by", "id"] 中的get的首字母不需要大写
arr[i].charAt(0).toUpperCase() 中charAt()是返回字符串中第几个字符,0代表返回第一个字符

toUpperCase转化为大写,arr[i].charAt(0).toUpperCase() 得到的是 E B I

+ arr[i].slice(1) 的意思是拼接E B I ,因为要拼接E B I,除了第一个get,其他都少了一个字母,所以是split(1)

var arr = arr.join("") :关键是join方法,将数组放入一个字符串中,默认是以逗号隔开,写了"" 字符串就是一坨

 

20、有这样一个 URL: http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e,请写 一段 JS 程序提取 URL 中的各个 GET 参数(参数名和参数个数不确定),将其按 key-value 形式返回到一个 json 结构中,如{a: "1", b: "2", c: "", d: "xxx", e: undefined}

var url = "http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e";

var paramstr = url.substring(url.indexOf("?")+1);

var pms = paramstr.split("&");

var obj = {};

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

   if(pms[i].indexOf("=") < 0){

      obj[pms[i]] = "undefined";

   }else{

      var pa = pms[i].split("=");

      obj[pa[0]] = pa[1];

   }

}

//只有 Gecko 核心的浏览器(好比 Firefox)支持该方法,也就是说 IE、Safari、Chrome、Opera 等浏览器均不支持该方法。

alert(obj.toSource());

 

21、var numberArray = [3,6,2,4,1,5]; (考察基础 API)

1、 实现对该数组的倒排,输出[5,1,4,2,6,3]
var numberArray = [3,6,2,4,1,5]; 
alert( numberArray .reverse())

 

2、实现对该数组的降序排列,输出[6,5,4,3,2,1]
var numberArray = [3,6,2,4,1,5]; 
// a-b 输出从小到大排序,b-a 输出从大到小排序。 
numberArray .sort(function(a,b){ 
return b-a}); 
console.log((numberArray .join()));

 

22.输出今天的日期,以 YYYY-MM-DD 的方式,比如今天是 2014 年 9 月 26 日,则输出 2014-09-26

var d = new Date();
// 获取年,getFullYear()返回 4 位的数字
var year = d.getFullYear();
// 获取月,月份比较特殊,0 是 1 月,11 是 12 月
var month = d.getMonth() + 1;
// 变成两位
month = month < 10 ? '0' + month : month;
// 获取日
var day = d.getDate();
day = day < 10 ? '0' + day : day;
alert(year + '-' + month + '-' + day); }

23.把两个数组合并,并删除第二个元素。

var array1 = ['a','b','c'];
var bArray = ['d','e','f'];
var cArray = array1.concat(bArray);
cArray.splice(1,1);
 

 

24、写一个 function,清除字符串前后的空格。(兼容所有浏览器)

if (!String.prototype.trim) {
            String.prototype.trim = function () {
                return this.replace(/^\s+/, "").replace(/\s+$/, "");
                //\s 匹配空白字符:回车、换行、制表符 tab 空格
            }
        }
        // test the function
        var str = " \t\n test string ".trim(); alert(str == "test string"); // alerts "true"

 

25、截取字符串 abcdefg 的 efg

alert('abcdefg'.substring(4));

 

26、判断一个字符串中出现次数最多的字符,统计这个次数

var str = 'asdfssaaasasasasaa';
var json = {};
for (var i = 0; i < str.length; i++) {
    if(!json[str.charAt(i)]){
       json[str.charAt(i)] = 1;
    }else{
       json[str.charAt(i)]++;
    }
};
var iMax = 0;
var iIndex = '';
for(var i in json){
    if(json[i]>iMax){
         iMax = json[i];
         iIndex = i;
    }
}        
console.log('出现次数最多的是:'+iIndex+'出现'+iMax+'次');

 

27、将数字 12345678 转化成 RMB 形式 如: 12,345,678

//思路:先将数字转为字符, 
str = str + '';
//利用反转函数,每三位字符加一个 ','最后一位不加; re()是自定义的反转函数,最后再反转回去! 
for (var i = 1; i <= re(str).length; i++) {
    tmp += re(str)[i - 1];
    if (i % 3 == 0 && i != re(str).length) {
        tmp += ',';
    }
}

28、Split()和 join()的区别?

Split()是把一串字符(根据某个分隔符)分成若干个元素存放在一个数组里
即切割成数组的形式;
join() 是把数组中的字符串连成一个长串,可以大体上认为是 Split()的逆操作

29、JavaScript 中如何对一个对象进行深度 clone?

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>深克隆</title>
    <script> function clone(obj) {
        if (typeof obj == 'object') {
            if (obj instanceof Array) {
                var result = [];
                for (var i = 0; i < obj.length; i++) {
                    result[i] = clone(obj[i]);
                }
                return result;
            } else {
                var result = {};
                for (var i in obj) {
                    result[i] = clone(obj[i]);
                }
                return result;
            }
        } else {
            return obj;
        }
    }

    var obj1 = [12, {a: 11, b: 22}, 5];
    var obj2 = clone(obj1);
    obj2[1].a += 5;
    console.log(obj1, obj2); </script>
</head>
<body></body>
</html>

30、js 数组去重,能用几种方法实现

1、使用 es6 set 方法
[...new Set(arr)] let arr = [1,2,3,4,3,2,3,4,6,7,6]; let unique = (arr)=> [...new Set(arr)]; unique(arr);//[1, 2, 3, 4, 6, 7]
2、利用新数组 indexOf 查找 indexOf() 方法可返回某个指定的元素在数组中首次出现的位置。 如果没有就返回-1。
3、for 双重循环 通过判断第二层循环,去重的数组中是否含有该元素,如果有就退出第二层循环,如果没有 j==result.length 就相等,然后把对应的元素添加到最后的数组 里面。
let arr = [1, 2, 3, 4, 3, 2, 3, 4, 6, 7, 6];
let result = [];
for (var i = 0; i
< arr.length;
     i++) {
    for (var j = 0
        ; j < result.length; j++) {
        if (arr[i] === result[j]) {
            break;
        }
        ;
    }
    ;
    if (j == result.length) {
        result.push(arr[i]);
    }
    ;
}
;
console.log(result);
4、利用 for 嵌套 for,然后 splice 去重
functionunique(arr)
{
    for (vari = 0; i < arr.length;
         i++) {
        for (varj = i + 1; j < arr.length; j++) {
            if (arr[i] == arr[j]) {
//第一个等同于第二个,splice 方法删除第二个 arr.splice(j,1);
                j--;
            }
        }
    }
    returnarr;
}
5、利用 filter
let arr = [1, 2, 3, 4, 3, 2, 3, 4, 6, 7, 6];
let unique =
    (arr) => {
        return arr.filter((item, index) => {
            return arr.indexOf(item) === index;
        })
    };
unique(arr);
6、
let arr = [1, 2, 3, 4, 3, 2, 3, 4, 6, 7, 6];
let unique
    = (arr) => {
    return arr.filter((item, index) => {
        return arr.indexOf(item) === index;
    })
};
unique(arr);
7、利用 Map 数据结构去重
let arr = [1, 2, 3, 4, 3, 2, 3, 4, 6, 7, 6];
let unique = (arr) => {
    let
        seen =
            new Map();
    return
    arr.filter((item) => {
        return !seen.has(item) && seen.set(item, 1);
    });
};
unique(arr);

31、谈谈你对 Javascript 垃圾回收机制的理解?

1、标记清除(mark and sweep)
这是 JavaScript 最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量, 垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开 环境” 垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被 环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了
2、引用计数(reference counting)
在低版本 IE 中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引 用计数的策略是跟踪记录每个值被使用的次数,当声明了一个 变量并将一个引用类型赋值给该 变量的时候这个值的引用次数就加 1,如果该变量的值变成了另外一个,则这个值得引用次数减 1,当这个值的引用次数变为 0 的时 候,说明没有变量在使用,这个值没法被访问了,因此可以 将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为 0 的值占用的空间 在 IE 中虽然 JavaScript 对象通过标记清除的方式进行垃圾回收,但 BOM 与 DOM 对象却是通过引 用计数回收垃圾的,也就是说只要涉及 BOM 及 DOM 就会出现循环引用问题

32、Class 和普通构造函数有何区别?

Js 构造函数:

//Js 构造函数:
function MathHandle(x, y) {
    this.x = x
    this.y = y
}

MathHandle.prototype.add = function () {
    Return
    this.x + this.y
}
var m = new MathHandle(1, 2)
console.log(m.add())

// class 基本语法: 
class MathHandle {
    constructor(x, y) {
        this.x = x
        this.y = y
    }

    add() {
        return this.x + this.y
    }
}

const m = new MathHandle(1, 2)
console.log(m.add())
语法糖:
在上述两段代码中分别加入如下代码,运行
console.log(typeof MathHandle) // 'function'
console.log(MathHandle.prototype.constructor === MathHandle) //true
console.log(m.__proto__ === MathHandle.prototype) //true
运行结果一致。我认为,class 是构造函数的语法糖
综上所述:
  • Class 在语法上更加贴合面向对象的写法
  • Class 实现继承更加易读、易理解
  • 更易于写 java 等后端语言的使用
  • 本质还是语法糖,使用 prototype

33、什么是 js 事件循环 event loop

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为 Event Loop(事件循环)

34、JS 里垃圾回收机制是什么,常用的是哪种,怎么处理的?

JS 的垃圾回收机制是为了以防内存泄漏,内存泄漏的含义就是当已经不需要某块内存时 这块内存还存在着,垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它
们所指向的内存
JS 中最常见的垃圾回收方式是标记清除
工作原理:
是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将 其标记为“离开环境”。标记“离开环境”的就回收内存
工作流程:
垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记
去掉环境中的变量以及被环境中的变量引用的变量的标记
再被加上标记的会被视为准备删除的变量
垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间

35、计算字符串字节数:

new function (s) {
    if (!arguments.length || !s) return null;
    if ("" == s) return 0;
    var l = 0;
    for (var i = 0; i < s.length; i++) {
        if (s.charCodeAt(i) > 255) l += 2; else l += 1;
        //charCodeAt()得到的是 unCode 码 } 
        // 汉字的 unCode 码大于 255bit 就是两个字节 
        alert(l);
    }
    ("hello world!");

36、js 如何处理防抖和节流

在进行窗口的 resize、scroll,输入框内容校验等操作时,如果事件处理函数调用的频率无限 制,会加重浏览器的负担,导致用户体验非常糟糕
此时我们可以采用 debounce(防抖)和 throttle(节流)的方式来减少调用频率,同时又不 影响实际效果
函数防抖:
函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数 才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时
如下,持续触发 scroll 事件时,并不执行 handle 函数,当 1000 毫秒内没有触发 scroll 事件时, 才会延时触发 scroll 事件
function debounce(fn, wait) {
    var timeout = null;
    return function () {
        if (timeout !== null) clearTimeout(timeout);
        timeout
            = setTimeout(fn, wait);
    }
}

//处理函数

function handle() {
    console.log(Math.random());
}

// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000));
函数节流
函数节流(throttle):当持续触发事件时,保证一定时间段内只调用一次事件处理函数 节流通俗解释就比如我们水龙头放水,阀门一打开,水哗哗的往下流,秉着勤俭节约的优
良传统美德,我们要把水龙头关小点,最好是如我们心意按照一定规律在某个时间间隔内 一滴一滴的往下滴
如下,持续触发 scroll 事件时,并不立即执行 handle 函数,每隔 1000 毫秒才会执行一次 handle函数
var throttle = function (func, delay) {
    var prev = Date.now();
    return function () {
        var
            context
                = this;
        var
            args
                =
                arguments;
        var
            now
                =
                Date.now();
        if (now
            - prev >= delay) {
            func.apply(context, args);
            prev =
                Date.now();
        }
    }
}

function handle() {
    console.log(Math.random());
}

window.addEventListener('scroll', throttle(handle, 1000));
总结:
  • 函数防抖:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在 delay 时间后 触发函数,但是在 delay 时间内再次触发的话,就会取消之前的计时器而重新设置。这样一 来,只有最后一次操作能被触发
  • 函数节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函 数
区别:

函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理 函数,而函数防抖只是在最后一次事件后才触发一次函数。比如在页面的无限加载场景下, 我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流 技术来实现

37、Eval 是做什么的?

1、eval()的作用
把字符串参数解析成 JS 代码并运行,并返回执行的结果;
例如:
1.1)eval("2+3");//执行加运算,并返回运算值。
1.2)eval("varage=10");//声明一个 age 变量
2、eval 的作用域在它所有的范围内容有效
例如 1:
functiona(){
    eval("var x=1"); //等效于 var x=1;
    console.log(x); //输出 1
}
a();
console.log(x);//错误 x 没有定义
示例 2:
functiona(){
    window.eval("var x=1"); // 等效于 window.x=1;定义了全局变量
    console.log(x); //输出 1
}
a();
console.log(x);//输出 1
3、注意事项
应该避免使用 eval,不安全,非常耗性能(2 次,一次解析成 js 语句,一次执行)
在 IE8 及 IE8 一下的版本就不支持了
4、其它作用
由 JSON 字符串转换为 JSON 对象的时候可以用 eval,例如:
  • 4.1)varjson="{name:'Mr.CAO',age:30}";
  • 4.2)varjsonObj=eval("("+json+")");
  • 4.3)console.log(jsonObj);

 38、什么是进程、什么是线程、它们之间是什么关系

1、进程:
  • 1.1)程序执行时的一个实例
  • 1.2)每个进程都有独立的内存地址空间
  • 1.3)系统进行资源分配和调度的基本单位
  • 1.4)进程里的堆,是一个进程中最大的一块内存,被进程中的所有线程共享的,进程 创建时分配,主要存放 new 创建的对象实例
  • 1.5)进程里的方法区,是用来存放进程中的代码片段的,是线程共享的
  • 1.6)在多线程 OS 中,进程不是一个可执行的实体,即一个进程至少创建一个线程 去执行代码
2、线程:
  • 2.1)进程中的一个实体
  • 2.2)进程的一个执行路径
  • 2.3)CPU 调度和分派的基本单位
  • 2.4)线程本身是不会独立存在
  • 2.5)当前线程 CPU 时间片用完后,会让出 CPU 等下次轮到自己时候在执行
  • 2.6)系统不会为线程分配内存,线程组之间只能共享所属进程的资源
  • 2.7)线程只拥有在运行中必不可少的资源(如程序计数器、栈)
  • 2.8)线程里的程序计数器就是为了记录该线程让出 CPU 时候的执行地址,待再次分配 到时间片时候就可以从自己私有的计数器指定地址继续执行
  • 2.9)每个线程有自己的栈资源,用于存储该线程的局部变量和调用栈帧,其它线程无权访问
3、关系:
  • 3.1)一个程序至少一个进程,一个进程至少一个线程,进程中的多个线程是共享进 程的资源
  • 3.2)Java 中当我们启动 main 函数时候就启动了一个 JVM 的进程,而 main 函数所在线程就是这个进程中的一个线程,也叫做主线程
  • 3.3)一个进程中有多个线程,多个线程共享进程的堆和方法区资源,但是每个线程 有自己的程序计数器,栈区域

39、什么是任务队列?

任务队列(task queue)主要分两种:
1、宏任务(macrotask):在新标准中叫 task
  • 1.1)主要包括:script(整体代码),setTimeout,setInterval,setImmediate,I/O,ui rendering
2、微任务(microtask):在新标准中叫 jobs
  • 2.1)主要包括:process.nextTick, Promise,MutationObserver(html5 新特性)

3、扩展:

  • 3.1)同步任务:在主线程上,排队执行的任务,只有前一个任务执行完毕,才能执 行后一个任务
  • 3.2)异步任务:不进入主线程,而进入“任务队列”的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行

40、栈和队列的区别?

  • 1、栈的插入和删除操作都是在一端进行的,而队列的操作却是在两端进行的
  • 2、队列先进先出,栈先进后出
  • 3、栈只允许在表尾一端进行插入和删除,而队列只允许在表尾一端进行插入,在表头一端 进行删除#

41、栈和堆的区别?

  • 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。 堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收
  • 2、堆(数据结构):堆可以被看成是一棵树,如:堆排序; 栈(数据结构):一种先进 后出的数据结构

 

 

  • 6
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值