js面试题

JS同步和异步的区别
其实同步和异步,无论如何,做事情的时候都是只有一条流水线(单线程),同步和异步的差别就在于这条流水线上各个流程的执行顺序不同。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行。

怎么移除、复制、创建、和查找节点
创建节点:createDocumentFragment(),createElement(),createTextNode()
移除节点:removeChild()
替换节点:replaceChild()
插入节点:insertBefore()
查找节点:getElementByTagName(),getElementsByName(),getElementById()

数组去重
1)利用ES6 SET去重
2)利用双重for循环去重
3)利用indexOf去重(新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组)
4)利用sort,sort()排序方法,然后根据排序后的结果进行遍历及相邻元素比对。

列举几种解决跨域的问题
1)通过jsonp跨域
通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。
2)Nodejs中间插件代理跨域
利用node + webpack + webpack-dev-server代理接口跨域。在开发环境下,由于vue渲染服务和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域,无须设置headers跨域信息了。
webpack.config.js部分配置:

devServer: {
	historyApiFallback: true,
	proxy: [{
		context: '/login',
		target: 'http://www.domain2.com:8080',  // 代理跨域目标接口
		changeOrigin: true,
		secure: false,  // 当代理某些https服务报错时用
		cookieDomainRewrite: 'www.domain1.com'  // 可以为false,表示不修改
	}],
	noInfo: true
}

谈谈垃圾回收机制的方式及内存管理
JS内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中)。其中栈存放变量,堆存放复杂对象,池存放常量。

JS中的基础数据类型,这些值都有固定的大小,往往都保存在栈内存中(闭包除外),由系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值,因此基础数据类型都是按值访问。数据在栈内存中的存储与使用方式类似于数据结构中的堆栈数据结构,遵循后进先出的原则。

JS的引用数据类型,比如数组Array。引用数据类型的值是保存在堆内存中的对象。
JS不允许直接访问堆内存中的位置,因此我们在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以粗浅地理解为保存在栈内存中的一个地址,该地址与堆内存的实际值相关联。

JS环境中分配的内存一般有如下生命周期:
1.内存分配:当我们申明变量、函数、对象的时候,系统会自动为他 们分配内存
2.内存使用:即读写内存,也就是使用变量、函数等
3.内存回收:使用完毕,由垃圾回收机制自动回收不再使用的内存

垃圾回收
JavaScript有自动垃圾收集机制,就是找出不再继续使用的值,然后释放其占用的内存。垃圾收集器会每隔固定的时间段就执行一次释放操作。

标记清除:垃圾回收器会在运行时给存储在内存中的所有变量加一个标记,然后去除环境中的变量以及被环境中的变量所引用的变量(闭包)在这些完成后仍存在标记的就是要删除的变量了,因为环境中的变量已经无法访问到这些变量了。

引用计数:引用计数的策略是跟踪记录每个值被使用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,这个值得引用次数就加一,如果该变量的值变成了另一个,则这个值得引用次数就减一,当这个值的引用次数为0的时候,说明没有变量在使用,这个值无法访问。由此可以将其占用的空间回收,这些垃圾回收器就会在运行时清理掉引用次数为0的值占用的空间,但这种方法容易引起内存泄漏,因为这种方式没有解决循环引用的问题,所以不建议使用!

闭包函数
当函数执行完成后,返回一个值时,当前执行环境会被清理掉。但是当函数返回一个调用了局部变量的函数时,外层函数的执行环境不会被清理掉,此作用域内的变量仍能被返回的函数引用。由此形成的即为闭包。

优点:对于可能重复的运算,进行缓存;
缺点:当运算数字过大时,会产生内存占用;影响整体性能;

function closure() {
     let n = 0;
     return {
         add: (m = 1 ) => n+=m,
         minus: (m = 1) => n-=m,
         getN: () => n
     }
 }
 
 // 外层作用域无法直接获取和修改 n; 但是可以通过 closure 返回的函数,进行获取和操作。
 const {add, minus, getN} = closure()
getN(); // 0
add(2);
getN(); // 2
minus();
getN(); // 1

js有哪几种数据类型

六大主要类型
• string
• number
• boolean
• null
• undefined
• object

九大内置对象
• String
• Number
• Boolean
• Object
• Function
• Array
• Date
• RegExp
• Error

null表示"没有对象",即该处不应该有值。用法是:
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。

undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。

如何判断数据类型typeof
stirng => ‘string’
number => ‘number’
boolean => ‘boolean’
symbol => ‘symbol’
function => ‘function’
undifine=>‘undifine’
null=>‘object’
NAN=>‘number’
其他对象都是’object’

js判断数据类型的方式

  1. typeof上个有讲,判断的是原型的
  2. instanceof 判断A是否是B的实例,内部机制是判断对象的原型链中是否有类型的原型。
let a = [] instanceof Array // true
let b = [] instanceof Object // true
let d = "123" instanceof String // false
let e = new String(123) instanceof String // true
  1. proto原型判断
let a = []
let a2 = a.__proto__ === Array.prototype // true
  1. construction指向的构造函数判断
let a3 = a.constructor === Array // true
let a4 = a.constructor.name === 'Array' // true
  1. typeof判断,注意虽然null是基本类型,但是typeof判断出来的是object
console.log(typeof(null)) // object
console.log(typeof('123')) // string
console.log(typeof(true)) // boolean
console.log(typeof(undefined)) // undefined
console.log(typeof(123)) // number
console.log(typeof([])) // object

事件代理
事件代理:又称事件委托,是javaScript中绑定事件的常用技巧。顾名思义,‘事件代理’就是把原本需要绑定的事件委托给父元素,让父元素负责事件监听。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好处是可以提高性能 。

apply()和call()方法
call()方法是预定义的javaScript方法,可以用来用来调用所有者对象作为参数的方法,也就是可以使用属于另一个对象的方法。
下列代码就是person1调用属于person对象的fullName()方法。

var person = {
    fullName: function() {
        return this.firstName + " " + this.lastName;
    }
}
var person1 = {
    firstName:"Bill",
    lastName: "Gates",
}
person.fullName.call(person1);  // 将返回 "Bill Gates"

call()也可以接收参数,也就是person1调用了person的fullname("seattle", "USA")

var person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"Bill",
  lastName: "Gates"
}
person.fullName.call(person1, "Seattle", "USA");

apply()call()不同的就是call() 方法分别接受参数。apply() 方法接受数组形式的参数。
call(person, a1, a2, ...)
apply(person, [a1, a2, a3])

js继承
(1) 原型链继承
当实现继承后,旧原型的实例属性,变成了新原型的原型属性,然后该原型的引用类型属性会被所有的实例共享,这样继承原型引用类型属性的实例之间不再具有自己的独特性了。

function SuperType(){
      this.colors=["red", "blue", "green"];
}

function SubType(){
      //继承SuperType
      SuperType.call(this);
}
var instance1=new SubType();
instance1.colors.push("black");
alert(instance1.colors);  //red,bllue,green,black

var instance2=new SubType();
alert(instance2.colors);  //red,blue,green

(2) 构造函数继承
所有的类型都只能使用构造函数模式(因为超类型的原型中定义的方法对于子类型不可见),因此方法都在构造函数中定义,函数复用就无从谈起了。

function SuperType() {
	this.colors = ["red", "blue", "green"];
}

function SubType() {
	//继承SuperType
	SuperType.call(this);
}

var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green" 

(3) 组合继承
整合了原型链继承和构造函数继承的优点

function SuperType(name) {
	this.name = name;
	this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function() {
	alert(this.name);
}

function SubType(name, age) {
	// 继承属性
	SuperType.call(this, name);
	this.age = age;
}

// 继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
	alert(this.age);
};

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27 

(4) 原形继承
原型的引用类型属性会在各实例之间共享

var person = {
 name: "Nicholas",
 friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" 

(5) 寄生式继承
由于寄生式继承为对象添加函数不能做到函数复用,因此效率降低。

function createAnother(original) {
	var clone = object(original);
	clone.sayHi = function() {
		alert("hi");
	};
	return clone;
}

(6) 寄生组合式继承

function object(o) {
	function F(){}
	F.prototype = o;
	return new F();
}

function inheritPrototype(superType, subType) {
	var prototype = object(superType.prototype);
	prototype.constructor = subType;
	subType.prototype = prototype;
}

function SuperType(name) {
	this.name = name;
	this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function() {
	alert(this.name);
};

function SubType(name, age) {
	SuperType.call(this, name);
	this.age = age;
}

inheritPrototype(SuperType, SubType);	// 这一句,替代了组合继承中的SubType.prototype = new SuperType()

SubType.prototype.sayAge = function() {
	alert(this.age);
};

js判断是否是数组
(1)instanceof 只适用于与数组初始化在相同上下文中才有效,且不能跨iframe
(2)Array.isArray()
(3)constructor constructor是不保险的,因为constructor属性是可以被修改的,会导致检测出的结果不正确,且不能跨iframe。
(4)proto

let a = [1, 2]
let b = Array.isArray(a)  // true
let c = a.__proto__ === Array.prototype  // true
let d = a instanceof Array // true
let e = a.constructor === Array

eval()
eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。

defer和async的区别
1.defer 和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)
2.defer,按照加载顺序执行脚本的,async 则是无序加载执行脚本的

严格模式的限制
1.不允许使用未声明的变量
2.不允许删除变量或对象、函数。delete
3.不允许变量重名
4.不允许使用八进制
5.不允许使用转义字符
6.不允许对只读属性赋值
7.不允许对一个使用getter方法读取的属性进行赋值
8.不允许删除一个不允许删除的属性
9.禁止this关键字指向全局对象

typeof 和 instanceof的区别
typeof:判断数据的类型
instanceof:判断数据的实例

prototype和__proto__的区别
prototype:每个函数都有一个prototype属性,这个属性指向函数的原型对象。
_proto__:这是每个对象(除null外)都会有的属性,叫做__proto__,这个属性会指向该对象的原型

绑定多个事件

 var btn4 = document.getElementById("btn4");
 btn4.addEventListener("click",hello1);
 btn4.addEventListener("click",hello2);
 function hello1(){
   alert("hello 1");
 }
function hello2(){
  alert("hello 2");
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值