前端面试题整理——javaScript部分
(1)typeof 和 instanceof
1、typeof 对于基本数据类型(boolean、null、undefined、number、string、symbol)来说,除了 null 都可以显示正确的类型;对于对象来说,除了函数都会显示 object。
2、instanceof 是通过原型链来判断的。可以判断一个对象的正确类型,但是对于基本数据类型的无法判断。
3、instanceof能正确判断对象的原理:
通过判断对象的原型链中是不是能找到类型的原型 [].proto == Array.prototype
function myInstanceof(left, right) {
let prototype = right.prototype
left = left.__proto__
while (true) {
if (left === null || left === undefined)
return false
if (prototype === left)
return true
left = left.__proto__
}
}
instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。
4、相关笔试题:
1)JavaScript中如何检测一个变量是一个String类型?请写出函数实现。
var str = 'ssssssss';
typeof str === 'string'
str.constructor === String
(2)闭包
1、闭包是指有权访问另一个函数作用域中的变量的函数。
创建闭包的常见方式,就是在一个函数内部创建另一个函数。
function a(){
var num = 100;
function b(){
console.log(num);
}
return b;
}
var c = a();
c(); //100
如上所示:函数b可以访问到函数a中的变量,函数b就是闭包。
2、闭包的用途:1)读取函数内部的变量;2)让这些变量始终保存在内存中
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多,建议只在绝对必要时再考虑使用闭包。
3、相关面试题
1)循环中使用闭包解决‘var’定义函数的问题
for(var i = 1; i <= 5; i++){
setTimeout(function timer(){
console.log(i);
},2000)
}
这里因为setTimeout是异步执行,循环会先执行完毕,这时i等于6,然后就会输出5个6。解决方法如下所示:
第一种是使用let
for(let i = 1; i <= 5; i++){
setTimeout(function timer(){
console.log(i);
},2000)
}
第二种是使用闭包
for(var i = 1; i <= 5; i++){
(function(j){
setTimeout(function timer(){
console.log(j);
},2000)
})(i)
}
这里首先使用了立即执行函数将i传入函数内部,这个时候值就被固定在了参数j上面不会改变,当下次执行timer这个闭包的时候,就可以使用外部函数的变量j。
(3)原型和原型链
1、我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象。而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。prototype就是通过调动构造函数而创建的那个对象实例的原型对象。
使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。
示例:
function Person(){}
var p1 = new Person();
var p2 = new Person();
Person.prototype.name = 'CoCo';
console.log(p1.name); //CoCo
console.log(p2.name); //CoCo
Person构造函数下有一个prototype属性。Person.prototype就是原型对象,也就是实例p1、p2的原型。
2、在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性包含一个指向prototype属性所在函数的指针。
function Person(){}
console.log(Person.prototype.constructor === Person); //true
3、Firefox、Safari和Chrome在每个对象上都支持一个属性__proto__,这个属性对脚本是完全不可见的。__proto__用于将实例与构造函数的原型对象相连。
连接实例与构造函数的原型对象之间。
function Person(){}
var p1 = new Person();
console.log(p1.__proto__ === Person.prototype); //true
//isPrototypeOf:检测一个对象是否是另一个对象的原型。或者说一个对象是否被包含在另一个对象的原型链中
console.log(Person.prototype.isPrototypeOf(p1)); //true
//getPrototypeOf:返回对象__proto__指向的原型prototype
console.log(Object.getPrototypeOf(p1) == Person.prototype); //true
(4)call、apply、bind
每个函数都包含两个非继承而来的方法: appy()和call()。这两个方法的用途都是在特定的作用域中调用函数,然后可以设置调用函数的this指向。
1、apply()第一个参数是this所要指向的那个对象,如果设为null或undefined或者this,则等同于指定全局对象。二是参数(可以是数组也可以是arguments对象)。
var a = 1;
function fn1(){
console.log(this.a);
}
var obj = {
a:2
};
fn1(); //1
fn1.apply(obj); //2
fn1.call(obj);//2
fn1.bind(obj)();//2
2、call()方法可以传递两个参数。第一个参数是指定函数内部中this的指向(也就是函数执行时所在的作用域),第二个参数是函数调用时需要传递的参数。第二个参数必须一个个添加。
3、bind()方法可以传递两个参数。第一个参数是指定函数内部中this的指向,第二个参数是函数调用时需要传递的参数。第二个参数必须一个个添加。
4、三者区别:
1)都可以在函数调用时传递参数,call、bind方法需要直接传入,而apply方法可以以数组或者arguments的形式传入。
2)call、apply方法是在调用之后立即执行函数,而bind方法没有立即执行,需要将函数再执行一遍。
5、手写三种函数
1)apply
Function.prototype.myApply = function(context){
if(typeof this !== 'function'){
throw new TypeError('Error');
}
context = context || window;
context.fn = this;
let result;
if(arguments[1]){
result = context.fn(...arguments[1]);
}else{
result = context.fn();
}
delete context.fn;
return result;
};
2)call
Function.prototype.myCall = function(context){
if(typeof this !== 'function'){
throw new TypeError('Error');
}
context = context || window;
context.fn = this;
const args = [...arguments].slice(1);
const result = context.fn(...args);
delete context.fn;
return result;
};
3)bind
Function.prototype.myBind = function(context){
if(typeof this !== 'function'){
throw new TypeError('Error');
}
const _this = this;
const args = [...arguments].slice(1);
return function F(){
if(this instanceof F){
return new _this(...args,...arguments);
}
return _this.apply(context,args.concat(...arguments));
}
};
bind返回了一个函数,对于函数来说有两种方式调用,一种是直接调用,一种是通过new的方式:
直接调用,这里选择了apply的方式实现,因为因为 bind 可以实现类似这样的代码 f.bind(obj, 1)(2),所以我们需要将两边的参数拼接起来,于是就有了这样的实现 args.concat(...arguments)。
最后来说通过 new 的方式,对于 new 的情况来说,不会被任何方式改变 this,所以对于这种情况我们需要忽略传入的 this
(5)深拷贝浅拷贝
1、浅拷贝
let a = {
age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2
基本数据类型是数据拷贝,会重新开辟一个空间存放拷贝的值。对象类型在赋值的过程中其实是复制了地址,从而会导致改变了一方其他也都被改变的情况。通常在开发中我们不希望出现这样的问题,我们可以使用浅拷贝来解决这个情况。
1)概念:对于对象类型,浅拷贝就是对对象地址的拷贝,拷贝的结果是两个对象指向同一个地址,并没有开辟新的栈,修改其中一个对象的属性,另一个对象的属性也会改变。
2)实现:
a、通过Object.assign来实现。Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象。用于对象的合并,将源对象的所有可枚举属性复制到目标对象。
Object.assign 只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,所以并不是深拷贝。
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
b、通过展开运算符 ... 来实现浅拷贝
let a = {
age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1
c、循环实现
这里是浅拷贝,因为对象还是拷贝的地址。
var a = {
name:'coco',
age:33,
fn:function(){
console.log("111")
},
children:{
age:66
}
};
var b = {};
for(var i in a){
b[i] = a[i];
}
b.children.age = 100;
console.log(a);
console.log(b);
2、深拷贝
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = { ...a }
a.jobs.first = 'native'
console.log(b.jobs.first) // native
浅拷贝只解决了第一层的问题,如果接下去的值中还有对象的话,那么就又回到最开始的话题了,两者享有相同的地址。要解决这个问题,我们就得使用深拷贝了。
1)概念:对于对象类型,深拷贝会开辟新的栈,两个对象对应两个不同的地址,修改其中一个对象的属性,另一个对象的属性不会改变。
2)原理:深复制--->实现原理,先新建一个空对象,内存中新开辟一块地址,把被复制对象的所有可枚举的(注意可枚举的对象)属性方法一一复制过来,注意要用递归来复制子对象里面的所有属性和方法,直到子子.....属性为基本数据类型。
3)实现:
a、通过 JSON.parse(JSON.stringify(object)) 来解决
缺点:可以满足基本的深拷贝,但是对于正则表达式、函数类型则无法进行拷贝。它还会抛弃对象的constructor。
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
b、递归
var a = {
name:'kiki',
age:25,
children:{
name:'CoCo',
age:12
},
arr:['111','222'],
fn:function(){
console.log(1);
}
};
function copy(obj){
if(obj instanceof Array){
var newObj = [];
}else{
var newObj = {};
}
for(var i in obj){
if(typeof obj[i] == 'object'){
newObj[i] = copy(obj[i]);
}else{
newObj[i] = obj[i];
}
}
return newObj;
}
var b = copy(a);
console.log(a);
console.log(b);
c、jQuery.extend()
jQuery.extend([deep],target,object1,object2...),deep位Boolean类型,如果为true则进行深拷贝。
前端面试题整理——javaScript部分的更多相关文章
- 前端面试题整理—JavaScript篇(一)
1.JS的基本数据类型和引用数据类型有哪些,两者区别 基本数据类型->string.number.Boolean.null.undefined.symbol 引用数据类型->array.o ...
- 前端面试题整理—JavaScript篇(二)
1.使用js实现一个可持续的动画 2.实现一个可以自由拖动的悬浮框 3.实现一个倒计时效果 4.使用js仿写一个原生下拉列表框 5.创建10个<a>标签,点击的时候弹出对应的序号 6.实现 ...
- 前端面试题整理——Javascript基础
常见值类型: let a; //undefined let s = 'abc'; let n = 100; let b = true; let sb = Symbol('s'); let nn = N ...
- 前端面试题 之 JavaScript
昨天我们一起分享了关于html和css的面试题<前端面试题之Html和CSS>,今天我们来分享关于javascript有关的面试题.我面试的时候最害怕面试官问我js了,因为我真心不擅长这个 ...
- 【web前端面试题整理02】前端面试题第二弹袭来,接招!
前言 今天本来准备先了解下node.js的,但是,看看我们一个小时前与一个小时后的差距: 既然如此,我们继续来搜集我们的前端面试题大业吧!!! 特别感谢玉面小肥鱼提供哟,@玉面小飞鱼 题目一览 Jav ...
- 前端面试题(JavaScript)
(前端面试题大全,持续更新) 箭头函数特点?箭头函数和普通函数的区别 手写懒加载(考虑防抖和重复加载问题) 手写bind(为什么要加预参数,为什么要加new) apply, call, bind ne ...
- 【web前端面试题整理06】成都第一弹,邂逅聚美优品
前言 上周四回了成都,休息了一下下,工作问题还是需要解决的,于是今天去面试了一下,现在面试回来了,我感觉还是可以整理一下心得. 这个面试题整理系列是为了以后前端方面的兄弟面试时候可以得到一点点帮助,因 ...
- 2019届校招前端面试题整理——HTML、CSS篇
前言 2019届校招陆陆续续开始了,整理了一些高频的面试题. HTML部分 1. 什么是<!DOCTYPE>? DOCTYPE是html5标准网页声明,且必须声明在HTML文档的第一行.来 ...
- 【web前端面试题整理08】说说最近几次面试(水)
为什么换工作 换工作简单来讲一般会归纳为钱不够或者人不对,我们团队氛围很不错,所以基本就定位到钱不够了,而我更多是考虑到以后的职业发展,简单说来就是对以后几年的工作有想法,而这种想法实现不一定能在现在 ...
随机推荐
- ASP.NET MVC下使用AngularJs语言(九):日期时间处理与显示
当在angularjs去显示一个时间时,如原原本本去显示这个值,它将显示一个怪怪的字符串,其实它就是被系列化json之后的字符串.如:一个空值显示为日期时间: 如果非空值显示为日期时间的情形: 为了能 ...
- linux配置防火墙和重启防火墙
1.在linux系统里面找到并打开编辑配置防火墙的文件,执行命令: vi /etc/sysconfig/iptables. 2.在上面打开的文件里面加入一下语句: -A INPUT -m state ...
- Codeforces gym102152 K.Subarrays OR
传送:http://codeforces.com/gym/102152/problem/K 题意:给定$n(n\le10^5)$个数$a_i(a_i\le10^9)$,对于任一个子数组中的数进行或操作 ...
- JavaScript使用注意事项
1.jQuery和JS中页面加载完后执行方法的写法 (1)在javascript中用来执行页面加载中的操作时候,我们会使用 window.onload=function(){} window.onlo ...
- 使用NetTcpBinding,WCF服务未能被激活
我的WCF采用的是NetTcpBinding,使用时就会报错,换成BasicHttpBinding,就一切正常 The requested service, 'net.tcp://wcf.xxxxx. ...
- CSS 将一个页面平均分成四个部分(div)
在项目中遇到需求,数据监控页面需要同时显示4个板块内容,如下图: CSS 如何将一个页面平均分成四个部分(div)呢? <!DOCTYPE html> <html lang=&quo ...
- Android模拟微信主页面的Demo
Android模拟微信主页面的Demo 效果图如下: 项目结构图如下: ContanctFragment: package com.demo.moniwexin; import android.app ...
- Linux_CentOS-服务器搭建 <六>
修改MySQL编码: 二话不说先登录: mysql -u root -p 查看下神奇的mysql系统变量及其值: show variables like '%character%'; //记住分号哦, ...
- java-Timer类使用方法
Timer: public Timer()创建一个新计时器.相关的线程不 作为守护程序运行. 方法摘要: void schedule(TimerTask task, Date time) ...
- .Net实现微信公众平台开发接口(一) 之 “微信开发配置”
我们只要通过微信官方认证,成为开发者,才能实现微信提供的各种接口,否则即使调用了接口,微信也不会实现推送,功能也无法通过开发模式真正得到实现,所以需要正确配置微信信息,通过微信官方认证,成为开发者才可 ...