(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部分的更多相关文章

  1. 前端面试题整理—JavaScript篇(一)

    1.JS的基本数据类型和引用数据类型有哪些,两者区别 基本数据类型->string.number.Boolean.null.undefined.symbol 引用数据类型->array.o ...

  2. 前端面试题整理—JavaScript篇(二)

    1.使用js实现一个可持续的动画 2.实现一个可以自由拖动的悬浮框 3.实现一个倒计时效果 4.使用js仿写一个原生下拉列表框 5.创建10个<a>标签,点击的时候弹出对应的序号 6.实现 ...

  3. 前端面试题整理——Javascript基础

    常见值类型: let a; //undefined let s = 'abc'; let n = 100; let b = true; let sb = Symbol('s'); let nn = N ...

  4. 前端面试题 之 JavaScript

    昨天我们一起分享了关于html和css的面试题<前端面试题之Html和CSS>,今天我们来分享关于javascript有关的面试题.我面试的时候最害怕面试官问我js了,因为我真心不擅长这个 ...

  5. 【web前端面试题整理02】前端面试题第二弹袭来,接招!

    前言 今天本来准备先了解下node.js的,但是,看看我们一个小时前与一个小时后的差距: 既然如此,我们继续来搜集我们的前端面试题大业吧!!! 特别感谢玉面小肥鱼提供哟,@玉面小飞鱼 题目一览 Jav ...

  6. 前端面试题(JavaScript)

    (前端面试题大全,持续更新) 箭头函数特点?箭头函数和普通函数的区别 手写懒加载(考虑防抖和重复加载问题) 手写bind(为什么要加预参数,为什么要加new) apply, call, bind ne ...

  7. 【web前端面试题整理06】成都第一弹,邂逅聚美优品

    前言 上周四回了成都,休息了一下下,工作问题还是需要解决的,于是今天去面试了一下,现在面试回来了,我感觉还是可以整理一下心得. 这个面试题整理系列是为了以后前端方面的兄弟面试时候可以得到一点点帮助,因 ...

  8. 2019届校招前端面试题整理——HTML、CSS篇

    前言 2019届校招陆陆续续开始了,整理了一些高频的面试题. HTML部分 1. 什么是<!DOCTYPE>? DOCTYPE是html5标准网页声明,且必须声明在HTML文档的第一行.来 ...

  9. 【web前端面试题整理08】说说最近几次面试(水)

    为什么换工作 换工作简单来讲一般会归纳为钱不够或者人不对,我们团队氛围很不错,所以基本就定位到钱不够了,而我更多是考虑到以后的职业发展,简单说来就是对以后几年的工作有想法,而这种想法实现不一定能在现在 ...

随机推荐

  1. JQuery Mobile - input 属性为 number,maxlength不起作用如何解决?

    <input type="text"  maxlength="5" />   效果ok,当 <input type="number& ...

  2. Servlet案例1:用户登录

    数据库准备: CREATE DATABASE web; USE web; CREATE TABLE users( id INT PRIMARY KEY AUTO_INCREMENT, username ...

  3. tar与压缩详解

    .gz  gzip   gunzip(gzip -d) .zip .rar .bz2 gzip压缩文件不保留原文件 , 不能压缩目录 gzip   filename.x   用gzip压缩文件 gun ...

  4. callback vs async.js vs promise vs async / await

    需求: A.依次读取 A|B|C 三个文件,如果有失败,则立即终止. B.同时读取 A|B|C 三个文件,如果有失败,则立即终止. 一.callback 需求A: let read = functio ...

  5. 【2019北京集训测试赛(十三)】数据(sj) 冷静分析

    题目大意:给你一个代表区间$[1,n]$的线段树,问你随机访问区间$[1,n]$中的一个子区间,覆盖到的线段树节点个数的期望(需要乘上$\frac{n(n-1)}{2}$后输出). 数据范围:$n≤1 ...

  6. 通过Anaconda在Ubuntu16.04上安装 TensorFlow(GPU版本)

    一. 安装环境 Ubuntu16.04.3 LST GPU: GeForce GTX1070 Python: 3.5 CUDA Toolkit 8.0 GA1 (Sept 2016) cuDNN v6 ...

  7. oracle10g和oracle11g导入导出数据区别

    其中flxuser为用户名,flxuser为密码,file值为导入到数据库中的备份文件. oracle10g和oracle11g导入导出数据的命令方式大有不同: oracle10g导入数据: imp  ...

  8. 使用SonarCloud对.NET Core项目进行静态代码分析

    本文将介绍如何使用SonarCloud进行.NET Core项目的静态代码分析.SonarCloud是SonarQube提供的基于云的版本,特别针对于开源项目是免费的. 首先,在sonarcloud. ...

  9. Android Studio中设置一个按钮的不同点击触发事件

    my_day_model = (RelativeLayout) v.findViewById(R.id.my_day_model);my_day_pic = (ImageView) v.findVie ...

  10. Android_自适应布局

    1.屏幕相关概念1.1分辨率是指屏幕上有横竖各有多少个像素1.2屏幕尺寸指的是手机实际的物理尺寸,比如常用的2.8英寸,3.2英寸,3.5英寸,3.7英寸android将屏幕大小分为四个级别(smal ...