当前位置:   article > 正文

史上最全js面试题_js面经

js面经

js相关面试题

1.简述同步和异步的区别

同步和异步是在编程中用于描述任务执行方式的两个概念。

  • 同步(Synchronous)任务按照顺序依次执行,每个任务必须等待前一个任务完成后才能开始执行。执行任务时会阻塞程序的运行,只有当前任务执行完成后才能继续执行下一个任务。如果某个任务执行时间过长,会导致整个程序响应变慢或停滞。

  • 异步(Asynchronous)任务不需要等待前一个任务完成,可以并发地执行多个任务。当遇到耗时操作时,不会阻塞线程或程序的执行,而是继续执行后续任务。在异步任务执行过程中,程序可以继续执行其他任务或进行其他操作。异步任务通常使用回调函数、Promise、async/await 等机制来处理任务的完成和结果返回。

区别:

  • 执行方式:同步任务按照顺序依次执行,而异步任务可以并发执行。
  • 阻塞:同步任务会阻塞程序的执行,而异步任务不会阻塞程序的执行。
  • 处理机制:同步任务没有特定的处理机制,而异步任务通常使用回调函数、Promise、async/await 等机制处理任务的完成和结果返回。

优缺点:
同步编程的优点是代码逻辑简单,易于理解和调试;但缺点是执行时间较长的任务会阻塞程序执行,导致程序响应性较差。
异步编程的优点是能够提高程序执行效率和响应速度,对于耗时操作不会阻塞程序执行;但缺点是代码可读性较差,需要处理回调函数或使用其他异步编程机制,增加了代码复杂性。

在实际开发中,根据具体需求和场景来选择同步或异步编程方式,以获得更好的性能和用户体验。

2.怎么添加、移除、复制、创建、和查找节点

(1)创建新节点

createDocumentFragment() //创建一个DOM片段

createElement() //创建一个具体的元素

createTextNode() //创建一个文本节点

(2)添加、移除、替换、插入

appendChild()

removeChild()

replaceChild()

insertBefore()

(3)查找

getElementsByTagName() //通过标签名称

getElementsByName() //通过元素的Name属性的值

getElementById() //通过元素Id,唯一性

3.实现一个函数clone 可以对Javascript中的五种主要数据类型(Number、string、Object、Array、Boolean)进行复制

Object.prototype.clone=function(){
	var o= this.constructor===Array? [] : {};
	for(var e in this){
		o[e]= typeof this[e]==="object" ? this[e].clone() : this[e];
	}
	return o;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4.如何消除一个数组里面重复的元素

function qc(arr1){
	let arr = [];
	for( let i = 0; i < arr1.length; i++) {
		if( arr.indexOf(arr1[i]) == -1) {
			arr.push(arr1[i])
		}
	}
	return arr;
}
arr1 = ["1","1","3","5","2","24","4","4","a","a","b"];
		
console.log(qc(arr1));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

5. 写一个返回闭包的函数

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。闭包就是能够读取其他函数内部变量的函数。可以把闭包简单理解成”定义在一个函数内部的函数”。
闭包有三个特性:
1.函数嵌套函数;
2.函数内部可以引用外部的参数和变量;
3.参数和变量不会被垃圾回收机制回收。
闭包就是一个函数的返回值为另外一个函数,在outer外部可以通过这个返回的函数访问outer内的局部变量.

function outer(){
var val = 0;
return function (){
val += 1;
document.write(val + “
”);
};
}
var outObj = outer();
outObj();//1,执行val += 1后,val还在
outObj();//2
outObj = null;//val 被回收
var outObj1 = outer();
outObj1();//1
outObj1();//2

闭包会使变量始终保存在内存中,如果不当使用会增大内存消耗(如果上例中定义很多outer(),则内存中会保存很多val变量)。
javascript的垃圾回收原理:
(1)、在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收;
(2)、如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。
那么使用闭包有什么好处呢?使用闭包的好处是:
1.希望一个变量长期驻扎在内存中
2.避免全局变量的污染
3.私有成员的存在

6.使用递归完成1到100的累加

function sum(num) {
	if( num==1 ){
		return 1;
	}
	return num+sum(num-1);
}
console.log(sum(100))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

7.Javascript有哪几种数据类型

  1. 基本数据类型:
  • 字符串 String
  • 数字 Number
  • 布尔 Boolean
  1. 复合数据类型:
  • 数组 Array
  • 对象 Object
  1. 特殊数据类型:
  • Null 空对象
  • Undefined 未定义

8.如何判断数据类型

判断js中的数据类型的几种方法
判断js中的数据类型有一下几种方法:typeof、instanceof、 constructor、 prototype、 $.type()/jquery.type(),

接下来主要比较一下这几种方法的异同。

判断js中的数据类型的几种方法

  1. 最常见的判断方法:typeof
alert(typeof a)   ------------> string
alert(typeof b)   ------------> number
alert(typeof c)   ------------> object
alert(typeof d)   ------------> object
alert(typeof e)   ------------> function
alert(typeof f)   ------------> function
//其中typeof返回的类型都是字符串形式,需注意,例如:
alert(typeof a == "string") -------------> true
alert(typeof a == String) ---------------> false
//另外typeof 可以判断function的类型;在判断除Object类型的对象时比较方便。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2、判断已知对象类型的方法: instanceof

alert(c instanceof Array) ---------------> true
alert(d instanceof Date) 
alert(f instanceof Function) ------------> true
alert(f instanceof function) ------------> false
//注意:instanceof 后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支。
  • 1
  • 2
  • 3
  • 4
  • 5

3、根据对象的constructor判断: constructor

alert(c.constructor === Array) ----------> true
alert(d.constructor === Date) -----------> true
alert(e.constructor === Function) -------> true
//注意: constructor 在类继承时会出错

eg:
      function A(){};
      function B(){};
      A.prototype = new B(); //A继承自B
      var aObj = new A();
      alert(aobj.constructor === B) -----------> true;
      alert(aobj.constructor === A) -----------> false;
//而instanceof方法不会出现该问题,对象直接继承和间接继承的都会报true:
      alert(aobj instanceof B) ----------------> true;
      alert(aobj instanceof B) ----------------> true;
//言归正传,解决construtor的问题通常是让对象的constructor手动指向自己:
      aobj.constructor = A; //将自己的类赋值给对象的constructor属性
      alert(aobj.constructor === A) -----------> true;
      alert(aobj.constructor === B) -----------> false; //基类不会报true了;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

4、通用但很繁琐的方法: prototype

alert(Object.prototype.toString.call(a) ===[object String]) -------> true;
alert(Object.prototype.toString.call(b) ===[object Number]) -------> true;
alert(Object.prototype.toString.call(c) ===[object Array]) -------> true;
alert(Object.prototype.toString.call(d) ===[object Date]) -------> true;
alert(Object.prototype.toString.call(e) ===[object Function]) -------> true;
alert(Object.prototype.toString.call(f) ===[object Function]) -------> true;
//大小写不能写错,比较麻烦,但胜在通用。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5、万能的方法:jquery.type()

//如果对象是undefined或null,则返回相应的“undefined”或“null”。
jQuery.type( undefined ) === "undefined"
jQuery.type() === "undefined"
jQuery.type( window.notDefined ) === "undefined"
jQuery.type( null ) === "null"
//如果对象有一个内部的[[Class]]和一个浏览器的内置对象的 [[Class]] 相同,我们返回相应的 [[Class]] 名字。 (有关此技术的更多细节。 )
jQuery.type( true ) === "boolean"
jQuery.type( 3 ) === "number"
jQuery.type( "test" ) === "string"
jQuery.type( function(){} ) === "function"
jQuery.type( [] ) === "array"
jQuery.type( new Date() ) === "date"
jQuery.type( new Error() ) === "error" // as of jQuery 1.9
jQuery.type( /test/ ) === "regexp"
//其他一切都将返回它的类型“object”。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

9.console.log(1+‘2’)和console.log(1-‘2’)的打印结果

console.log(1+'2') //12
console.log(1-'2') //-1
  • 1
  • 2

10.Js的事件委托是什么,原理是什么

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
原理:事件委托是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开始,然后逐步向上传播事件。

11.如何改变函数内部的this指针的指向

  • 当我们使用一个函数需要改变this指向的时候才会用到call、apply、bind
  • 如果你要传递的参数不多,则可以使用fn.call(thisObj, arg1, arg2 …)
  • 如果你要传递的参数很多,则可以用数组将参数整理好调用fn.apply(thisObj, [arg1, arg2 …])
  • 如果你想生成一个新的函数长期绑定某个函数给某个对象使用,则可以使用const newFn = fn.bind(thisObj); newFn(arg1, arg2…)
  • call和apply第一个参数为null/undefined,函数this指向全局对象,在浏览器中是window,在node中是global

12.列举几种解决跨域问题的方式,且说明原理

  1. jsonp

script标签是不受同源策略影响的,它可以引入来自任何地方的js文件。动态添加script

  1. 使用window.name来进行跨域

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

  1. 使用HTML5中新引进的window.postMessage方法来跨域传送数据

window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。

13.谈谈垃圾回收机制的方式及内存管理

垃圾回收机制—GC

Javascript具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。

原理:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存

通常情况下有两种实现方式:标记清除和引用计数

  • 标记清除: js中最常用的垃圾回收方式就是标记清除。

当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。

  • 引用计数的含义是跟踪记录每个值被引用的次数

当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。

内存管理

1)、Javascript引擎基础GC方案是(simple GC):mark and sweep(标记清除),即:

  • (1)遍历所有可访问的对象。
  • (2)回收已不可访问的对象。

2)、GC的缺陷

和其他语言一样,javascript的GC策略也无法避免一个问题:GC时,停止响应其他操作,这是为了安全考虑。而Javascript的GC在100ms甚至以上,对一般的应用还好,但对于JS游戏,动画对连贯性要求比较高的应用,就麻烦了。这就是新引擎需要优化的点:避免GC造成的长时间停止响应。

3)、GC优化策略

1)分代回收(Generation GC)
这个和Java回收策略思想是一致的。目的是通过区分“临时”与“持久”对象;多回收“临时对象”区(young generation),少回收“持久对象”区(tenured generation),减少每次需遍历的对象,从而减少每次GC的耗时
2)增量GC
这个方案的思想很简单,就是“每次处理一点,下次再处理一点,如此类推”

14.写一个function ,清除字符串前后的空格

//重写trim方法
if(!String.prototype.trim){
    String.prototype.trim = function(){
        return this.replace(/^\s+/,"").replace(/\s+$/,""); 
    }
} 
//写fntrim去掉左右空格
function fntrim(str){
    return str.replace(/^\s+/,"").replace(/\s+$/,"");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

15.js实现继承的方法有哪些

既然要实现继承,那么首先我们得有一个父类,代码如下:

// 定义一个动物类
function Animal (name) {
  // 属性
  this.name = name || 'Animal';
  // 实例方法
  this.sleep = function(){
    console.log(this.name + '正在睡觉!');
  }
}
// 原型方法
Animal.prototype.eat = function(food) {
  console.log(this.name + '正在吃:' + food);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

原型链继承

核心:将父类的实例作为子类的原型

function Cat(){ 
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true 
console.log(cat instanceof Cat); //true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

特点:

  1. 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
  2. 父类新增原型方法/原型属性,子类都能访问到
  3. 简单,易于实现

缺点:

  1. 要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
  2. 无法实现多继承
  3. 来自原型对象的所有属性被所有实例共享(来自原型对象的引用属性是所有实例共享的)(详细请看附录代码:[示例1](javascript:void(0)
    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/792608
推荐阅读
相关标签