JavaScript考核详解


JavaScript考核详解

一、请简述var,let,const的区别?

  1. 块级作用域:块级作用域有大括号{ }包括,let和const具有块级作用域,var不存在块级作用域。块级作用域解决了ES6中的两个问题:
    • 内层变量可能覆盖外层变量
    • 用来计数的循环变量泄露为全局变量
  2. 变量提升:var存在变量提升,let和const不存在变量提升,即变量只能在声明之后使用,否则会报错。
  3. 给全局添加属性:浏览器的全局对象是window,Node的全局对象是global。var声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是let和const不会。
  4. 重复声明:var声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的变量。const和let不允许重复声明变量。
  5. 暂时性死区:在使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用var声明的变量不存在暂时性死区,因为var声明的变量都会被提升到作用域的最顶部。
  6. 初始值设置:在变量声明时,var 和 let 可以不用设置初始值。而const声明变量必须设置初始值。
  7. 指针指向:let和const都是ES6新增的用于创建变量的语法。 let创建的变量是可以更改指针指向(可以重新赋值)。但const声明的变量是不允许改变指针的指向。

二、解释垃圾回收机制,垃圾回收的方式?

  1. 垃圾回收的概念:JavaScript代码运行时,需要分配内存空间来储存变量和值。当变量不在参与运行时,就需要系统收回被占用的内存空间,这就是垃圾回收。

    回收机制

    • Javascript 具有自动垃圾回收机制,会定期对那些不再使用的变量、对象所占用的内存进行释放,原理就是找到不再使用的变量,然后释放掉其占用的内存。

    • JavaScript中存在两种变量:局部变量和全局变量。全局变量的生命周期会持续到页面卸载;而局部变量声明在函数中,它的生命周期从函数执行开始,直到函数执行结束,在这个过程中,局部变量会在堆或栈中存储它们的值,当函数执行结束后,这些局部变量不再被使用,它们所占有的空间就会被释放。

    • 不过,当局部变量被外部函数使用时,其中一种情况就是闭包,在函数执行结束后,函数外部的变量依然指向函数内部的局部变量,此时局部变量依然在被使用,所以不会回收。

      function outer(){
          let i = 1
          function fn(){
              console.log(i)
          }
          return fn
      }
      const fun = outer()
      fun() // 返回1
      // 外层函数使用内部函数的变量
      
  2. 垃圾回收的方式

    1. 引用计数法:IE采用的引用计数算法,定义“内存不再使用”,就是看一个对象是否有指向它的引用,没用引用了就回收对象。

      算法:

      1. 跟踪记录被引用的次数
      2. 如果被引用类一次,那么就记录次数加一,多次引用会累加
      3. 如果减少一个引用就减一
      4. 如果引用次数是0,则释放内存

      但引用计数法存在一个致命的问题:嵌套引用(循环引用)。

      如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露,因为他们的引用次数永远不会是0,这样的相互引用如果大量存在会导致大量的内存泄露。

    2. 标记清除法:现代的浏览器已经不再使用引用计数算法,使用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。

      核心:

      • 标记清除算法将“不再使用的对象”定义为“无法达到的对象”。
      • 就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。
      • 那些无法由根部出发的对象被标记为不再使用,稍后进行回收。

三、以下代码的输出是什么?

var tmp = new Date();

function fn(){
	console.log(tmp);
	if(false){
		var tmp = 'hello world';
	}
}

fn();

答案:undefined。

解释:var存在变量提升,var声明的变量会被提升到作用域的顶层,但该声明不包括赋值,所以虽然if判断里的语句虽然并不会执行,但其实已经在fn函数作用域内的顶部声明了一个未被赋值的tmp变量,所以console.log(tmp)打印出来的结果是undefined。

四、this的指向

var name = "window";
var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  },
   hello: () => console.log(this.name)
};
function sayName() {
  var sss = person.sayName;
  sss(); 
  person.sayName(); 
  (person.sayName)(); 
  (b = person.sayName)();
   person.hello()
}
sayName(); 

调用sss();后this指向window。

原因:当函数被作为普通函数调用时,this指向全局对象(在浏览器中是window,在Node.js中是global),或者在严格模式('use strict')下,thisundefined

调用person.sayName();后this指向person。

原因:当函数被作为对象的方法调用时,this指向该对象。

调用(person.sayName)();后this指向person。

原因:当函数被作为对象的方法调用时,this指向该对象。

调用(b = person.sayName)();后this指向window。

原因:当函数被作为普通函数调用时,this指向全局对象(在浏览器中是window,在Node.js中是global),或者在严格模式('use strict')下,thisundefined

调用后person.hello()this指向window

原因:箭头函数不绑定自己的this,它会捕获其所在上下文的this值,作为自己的this值。

五、实现数组扁平化

JavaScript中实现数组扁平化(即将多维数组转换为一维数组)有多种方法,以下是一些常见的方法:

  1. 使用递归

递归是处理嵌套结构如多维数组的一个直观方法。你可以编写一个函数,该函数检查数组中的每个元素,如果元素是数组,则递归调用该函数;否则,将元素添加到结果数组中。

function flattenArray(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      result = result.concat(flattenArray(arr[i]));
    } else {
      result.push(arr[i]);
    }
  }
  return result;
}

console.log(flattenArray([1, [2, [3, [4]], 5]])); // 输出: [1, 2, 3, 4, 5]
  1. 使用栈

迭代方法结合栈也是处理数组扁平化的有效方式。我们可以将数组元素逐一压入栈中,并在遇到数组时将其元素再次压入栈中,直到栈为空。

function flattenArrayWithStack(arr) {
  let stack = [...arr];
  let res = [];
  while (stack.length) {
    // 取出栈的最后一个元素
    const next = stack.pop();
    if (Array.isArray(next)) {
      // 如果是数组,则将数组元素逆序压入栈中
      // 这样可以保证原始数组的顺序在结果中被保留
      stack.push(...next.reverse());
    } else {
      // 否则,将元素添加到结果数组中
      res.push(next);
    }
  }
  // 因为我们是从栈中取元素,所以结果数组是逆序的,需要反转
  return res.reverse();
}

console.log(flattenArrayWithStack([1, [2, [3, [4]], 5]])); // 输出: [1, 2, 3, 4, 5]
  1. 使用ES6的扩展运算符和reduce

这种方法利用了ES6的扩展运算符...和数组的reduce方法,通过递归的方式实现扁平化。

function flattenArrayWithReduce(arr) {
  return arr.reduce((acc, val) => Array.isArray(val) ? [...acc, ...flattenArrayWithReduce(val)] : [...acc, val], []);
}

console.log(flattenArrayWithReduce([1, [2, [3, [4]], 5]])); // 输出: [1, 2, 3, 4, 5]
  1. 使用flat()(ES2019引入)

从ES2019开始,JavaScript引入了一个名为flat()的数组方法,它可以用来实现数组的扁平化,其默认只会扁平化一层,但你可以通过传递一个整数参数来指定扁平化的深度,如果深度为Infinity,则表示无论嵌套多少层都将被扁平化。

const arr = [1, [2, [3, [4]], 5]];

console.log(arr.flat(Infinity)); // 输出: [1, 2, 3, 4, 5]

这是最简单也最现代的方法,但请注意,它可能不适用于需要兼容较旧JavaScript环境的场景。

六、实现数组去重

题目:给定某无序数组,要求去除数组中的重复数字并且返回新的无重复数组。

解答:

  1. 使用ES6的Set数据结构

Set是一个构造函数,它创建一个值的集合,其中的值都是唯一的,没有重复的值。因此,将数组转换为Set,然后再转换回数组,可以实现去重。

function uniqueArray(arr) {
  return [...new Set(arr)];
}

console.log(uniqueArray([1, 2, 2, 3, 4, 4, 5])); // 输出: [1, 2, 3, 4, 5]
  1. 使用filter()indexOf()

遍历数组,对于当前元素,使用indexOf()检查它首次出现的位置是否和当前位置相同,如果相同,则说明不是重复元素,将其保留。

function uniqueArray(arr) {
  return arr.filter((item, index) => arr.indexOf(item) === index);
}

console.log(uniqueArray([1, 2, 2, 3, 4, 4, 5])); // 输出: [1, 2, 3, 4, 5]

但需要注意的是,这种方法在数组元素为对象时可能不适用,因为对象比较是基于引用的。

  1. 使用Map数据结构

类似于SetMap也是一个集合,但它保存键值对。我们可以利用它的键是唯一的特性来实现去重。

function uniqueArray(arr) {
  const map = new Map();
  arr.forEach(item => map.set(item, true));
  return Array.from(map.keys());
}

console.log(uniqueArray([1, 2, 2, 3, 4, 4, 5])); // 输出: [1, 2, 3, 4, 5]
  1. 使用reduce()

reduce()方法可以对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。我们可以利用这一点来构建一个新的数组,只包含不重复的元素。

function uniqueArray(arr) {
  return arr.reduce((acc, current) => {
    if (acc.indexOf(current) === -1) {
      acc.push(current);
    }
    return acc;
  }, []);
}

console.log(uniqueArray([1, 2, 2, 3, 4, 4, 5])); // 输出: [1, 2, 3, 4, 5]

七、JS中的基本类型

Number、Object、BigInt、Symbol、String、Boolean、Undefined、Null

  • 栈:原始数据类型(Undefined、Null、Boolean、Number、String)
  • 堆:引用数据类型(对象、数组和函数)

ES6新增:Symbol、Bigint

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值