Giter VIP home page Giter VIP logo

front-end-interview's People

Contributors

nokelong avatar

Watchers

 avatar

front-end-interview's Issues

箭头函数和普通函数的区别

1、箭头函数是匿名函数,不能作为构造函数,不能使用new

2、箭头函数不绑定arguments,取而代之用rest参数...解决

function A(a){
  console.log(arguments);
}
A(1,2,3,4,5,8);  //  [1, 2, 3, 4, 5, 8, callee: ƒ, Symbol(Symbol.iterator): ƒ]

let B = (b)=>{
  console.log(arguments);
}
B(2,92,32,32);   // Uncaught ReferenceError: arguments is not defined

let C = (...c) => {
  console.log(c);
}
C(3,82,32,11323);  // [3, 82, 32, 11323]

3、函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值。

function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到 100 毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。
4、箭头函数通过 call() 或 apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响

let obj2 = {
    a: 10,
    b: function(n) {
        let f = (n) => n + this.a;
        return f(n);
    },
    c: function(n) {
        let f = (n) => n + this.a;
        let m = {
            a: 20
        };
        return f.call(m,n);
    }
};
console.log(obj2.b(1));  // 11
console.log(obj2.c(1)); // 11

5、箭头函数没有原型属性

var a = ()=>{
  return 1;
}
function b(){
  return 2;
}
console.log(a.prototype);  // undefined
console.log(b.prototype);   // {constructor: ƒ}

6、不可以使用yield命令,因此箭头函数不能用作 Generator 函数

什么是深拷贝和浅拷贝

一、概念
1、浅拷贝
对于字符串类型,浅复制是对值的复制;对于引用类型(Objeact, Array 等)只拷贝指向对象的指针,没有复制对象本身,新旧对象使用同一个内存地址。
2、深拷贝
复制对象本身,新旧对象内存地址隔离,修改新对象不影响久对象。

二、实现
1)浅拷贝实现
浅拷贝实现主要包括三种方式遍历、Object.assgin、拓展运算符...
1、遍历方式

function extend(soruce) {
   var dist = {};
   for(var props in soruce) {
       if(soruce.hasOwnProperty(props)) {
          dist[props]  = soruce[props]
        }
     }
     return dist;
}

2、Object.assgin方式

let obj = {
    name: "tomcat",
    age: 2
}
let target = Object.assgin({}, obj);
console.log(target);

3、使用拓展运算...

var obj = {
  name: "tom",
  age: 18,
  sayhi() {
     console.log('hi')
  },
}
var newObj = {...obj}
console.log(newObj);

2)深拷贝实现
深拷贝实现基本2种方式,遍历和利用JSON.parse。
1、JSON.parse只支持Number, String, Boolean, Array, 扁平对象,不支持RegExp;改造了对象的contructor为object。

function deepClone(obj) {
    var target= {};
    try {
        target= JSON.parse(JSON.stringify(obj));
    }
    return target;
}

2、遍历对象

function deepClone(obj) {
  var target;
  if(typeof obj === 'object') {
    target = Array.isArray(obj) ? [] : {};
    for(var key in obj) {
      target[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
    }
  } else {
    target = obj;
  }
  return target;
}

另外一种实现

function extend(source, isDeep) {
	
	var isObjFun = function (name) {
		var toString = Object.prototype.toString;
		return function (){
			return toString.call(arguments[0]) === '[object ' + name + ']'
		}
	}
    var isObject = isObjFun('Object'),
        isArray = isObjFun('Array'),
        isBoolean = isObjFun('Boolean'),
        isFunction = isObjFun('Function');

	function _extend(source) {
        if (source === null || typeof source != 'object' && !isFunction(source)) {
        	return source;
        } 
        if (isFunction(source)){
            return new Function('return ' + source.toString())()
        } else {
            var  value , target = isArray(source) ? []: {};
             for(var prop in source) {
                value = source[prop];
                if (value == source) {
                	continue;
                }
                if (isDeep) {  //是否是深拷贝
                    if (isArray(value) || isObject(value)) {
                        target[prop] = extend(value, isDeep);
                    } else if(isFunction(prop)) {
                        target[prop] =new Function('return '+ value.toString())();
                    }
                } else {
                	target[prop]  = value
                }
         	}
         	return target;
            }
	}

	return _extend(source);
}

什么是包装类型

一、数据类型
  在JS中,数据的分类分为基本数据类型和引用类型;其中,引用类型有自己内置的方法同时可以定义其他方法来操作数据,而基本数据没有内置方法来操作数据。

  • 基本数据类型包括:undefined,null,boolean,numbr,string,symbol;

  • 引用类型:function,object。

二、包装类型
为了操作基本类型值,ECMAScripts提供了3个特殊的引用类型(基本包装类型)String,Number,Boolean。这些包装类型和其他引用类型相似,同时具备各自基本类型响应的特性。

var s1="some text";  
var s2=s1.substring(2);  
console.log(s2); // 'me text'

  s1作为字符串基本类型,从逻辑上讲它们不应该有方法(substring)方法,第二行代码应该报错才对。
当第二行代码访问 s1 时,访问过程处于一种读取模式,JS会自动执行下面的过程:
1、创建String类型的一个实例;
2、在实例上调用指定的方法;
3、销毁整个实例。

//用 String 构造函数创建一个实例,这个实例是一个对象
let str = new String('some text')
//对象中有内置方法供开发人员调用
let str2 = str.substring()
str = null

  经过此番处理,基本的字符串值就变得跟对象一样了,而且,上面这三个步骤也分别适用于Boolean 和 Number 类型对应的布尔值和数字值

  引用类型与基本包装类型的主要区别就是对象的生存期。使用 new 操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。

var s1="some text"  
s1.color="red";  
alert(s1.color);//undefined  

  在此,第二行代码为字符串s1添加一个 color 属性。当第三行代码在此访问 s1时,其 color 属性不见了。问题的原因就是第二行创建的 String 对象在执行第三行代码时已经被销毁了。第三行代码又创建自己的 String 对象,而该对象没有 color 属性,结果未undefined。

三、和同名转换函数区别
使用 new 调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的:

var value="25";  
var number=Number(value); //转型函数  
alert(typeof number); //"number"  

var obj=new Number(value); //构造函数  
alert(typeof obj); //"object"  

Proxy和object.defineProperty

Vue 3.0中响应式采用了基于ES2015的Proxy来代替Object.defineProperty。新的Proxy相对于defineProperty主要什么区别如下:

1、对监控到数组下标的变化
  Object.defineProperty无法监听数组变化,所以 Vue 才设置了 7 个变异数组(push、pop、shift、unshift、splice、sort、reverse)的 hack 方法来解决问题。但是从性能/体验的性价比考虑,Vue放弃了特性,vm.items[indexOfItem] = newValue (数组下标的变化)这种是无法检测。
Proxy支持对数组监听,还可以监听Map、Set、WeakSet、WeakMap,同时还实现了惰性的监听,不会在初始化的时候创建所有的Observer,而是会在用到的时候才去监听。

2、操作的对象
  Object.defineProperty只能劫持对象的属性,而 Proxy 是直接代理对象。
  Object.defineProperty 只能对属性进行劫持,需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。而 Proxy 直接代理对象,不需要遍历操作。

3、新增属性
  由于 Object.defineProperty劫持的是对象的属性,不能监听对象新添加的属性,只能监听初始定义的属性修改,所以新增属性时,需要重新遍历对象,对其新增属性再使用 Object.defineProperty 进行劫持。使用 Vue 给 data 中的数组或对象新增属性时,需要使用 vm.$set 才能保证新增的属性也是响应式的。
  Proxy 通过 set(target, propKey, value, receiver) 拦截对象属性的设置,是可以拦截到对象的新增属性的。

4、 Proxy支持 13 种拦截操作
  get(target, propKey, receiver):拦截对象属性的读取
  set(target, propKey, value, receiver):拦截对象属性的设置
  has(target, propKey):拦截 propKey in proxy 的操作,返回一个布尔值
  deleteProperty(target, propKey):拦截 delete proxy[propKey] 的操作
  参考链接:https://www.infoq.cn/article/spcmacrdazqfmlbgjegr

5、兼容性
  Object.defineProperty实现的代理,兼容主流浏览器和ie9以上的ie浏览器。
  主流的浏览器都支持Proxy,ie系列却还是不兼容

非阻塞脚本

非阻塞脚本
在高性能JavaScript中提到一种提高网页性能的方案,非阻塞脚本,即等页面加载完成后,再加载javascript脚步。实现非阻塞脚本的方式主要包括3种:

1、在scriptscript标签中添加defer和async 标识:
    那这2种的主要区别如下:defer属性标识此元素中包含的脚步不会修改DOM,可以稍后执行。defer标记<script>标签,表明js文件将在<script>标签解析的时候下载【不会阻塞浏览器其他处理过程】,直到DOM加载完成才执行,执行是在onload事件句柄被调用之前;

1、defer要等到整个页面正常渲染结束才会执行;

2、async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后再继续渲染。

3、defer是“渲染完再执行”,async是“下载完就执行”。

4、多个defer脚本,会按照它们在页面中出现的顺序加载,而多个async脚本是不能保证加载顺序。

下图可以直观的看出三者之间的区别
image

2、动态脚步元素
使用javascript动态创建HTML元素,见如下的代码demo:

 function  loadJS() {
      for (var i = 0; i < arguments.length; i++) {
           var p = arguments[i];
           var url = getUrl(p);
           document.write('<script src="'+ url + '"></script>');
     }
     getUrl: function (url, version) {
          var v = Math.ceil(new Date().getTime()/(1000*60*60*24));
          if (version) {
              return v;
            }
             return url + "?v=" + v;
         }
  }

js垃圾回收机制

前端都知道JavaScript中的垃圾回收是自动触发的,那什么是垃圾,垃圾回收的机制是怎么样的?
一、前言

程序运行需要占用机器的内存,当我们创建对象,方法调用计算都需要向机器申请内存。在c, c++语音中,需要开发者手动申请和回收内存,而在java,javascript中程序将自动进行内存管理。

二、什么是垃圾

在JavaScript中,不再使用的基本类型,不再应用的对象类型变量都视为程序中的垃圾,在程序启动垃圾回收时这些无用变量都将被清除掉。

三、垃圾回收机制

在JavaScript中垃圾回收有几种算法:标记清除引用计数双倍内存

1、双倍内存
在Google的V8引擎中,主要将内存分为新生代和老生代。其中新生代中存放存活时间较短的对象,老生代中存放存活时间较长或常住内存的对象。

对新生代内存中的对象,V8采用双倍内存的方法即将内存一分为二,每一部分空间称为semispace。在这2个semispace空间中,只有一个处于使用中称为From空间,另一个空间处于闲置状态称为To空间。
当我们分配对象时,先是在From空间进行分配。当进行垃圾回收时,,会检查From空间中存活对象,并将存活对象复制到To空间中,非存活对象直接销毁回收占用的内存。完成复制后,From空间和To空间的角色发生兑换。下一次垃圾回收继续上面的步骤。

双倍内存的缺点是只能使用内存中的一半,典型的牺牲空间换时间的算法,所以无法大规模的应用到所有的垃圾回收中。

2、标记清除
在Google的V8引擎中,对于老生代中的对象,由于存活对象占比较重,采用双倍内存方式会有2个问题:一是存活对象多,复制对象效率将降低;另一个问题是浪费一半的内存。
对于老生代中的对象,主要采用标记清除进行垃圾回收,它分为标记清除2个阶段。在标记阶段遍历内存中的所有对象,并标记所有存活对象,在清除阶段只清理没有被标记的对象。

标记清除方法

var m = 0,n = 19 // 把 m,n,add() 标记为进入环境。
add(m, n) // 把 a, b, c标记为进入环境。
console.log(n) // a,b,c标记为离开环境,等待垃圾回收。
function add(a, b) {
  a++
  var c = a + b
  return c
}

标记清除 Mark-Sweep方法在进行一次标记清除后,内存空间会出现不连续的情况。这种内存碎片对后续的内存分配造成分配问题。一种改进方式是标记整理 Mark-Compact标记整理 Mark-Compact在整理的过程中,将活着的对象往一端移动,移动完成后,直接清理掉边界外的内存。

3、引用计数
引用计数就是引擎中有一张“引用表”,保存了内存里面所有的资源(通常是各种值)的引用次数,如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。

标记清除方法
上图中,左下角的两个值,没有任何引用,所以可以释放。

参考资料: JavaScript中的垃圾回收和内存泄漏

使用let const var 声明变量的区别

let const 是ES6中新增的声明变量的关键字。
1、let
let 声明的变量具有如下特性:

  • 变量值在代码块有效,拥有块级作用域
  • 不存在变量提升
// let 的情况
console.log(b); // 报错ReferenceError
let b = 2;
  • 暂时性死域 ,let声明变量之前都不可用
if (true) {
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError
 
  let tmp; // TDZ结束
  console.log(tmp); // undefined
 
  tmp = 123;
  console.log(tmp); // 123
}
  • 不允许重复声明
  let a= 1;
  let a = 1;
// Uncaught SyntaxError: Identifier 'a' has already been declared

2、const
const 声明的变量具有如下特性:

  • 声明一个只读的常量,一旦声明,常量指向的内存地址不可改变
  • 简单类型数据Number,String,Boolean值不可改变
  • 复合类型数据Array,Object内存地址指针不可改变
  • 块级作用域,不存在变量提升
  • 不允许重复声明变量
const num = 10; 
num = 11; // Uncaught TypeError: Assignment to constant variable
const obj = {a: 1};
obj.b = 2;
console.log(obj); // {a: 1, b: 1}
<!--对obj 重新赋值-->
obj = {c: 3};  // Uncaught TypeError: Assignment to constant variable

3、var
var 声明的变量具有如下特性:

  • 允许重复声明变量
// var 的情况
var a =1;
var a = 2;
console.log(a); // 输出2
  • 变量存在变量提升,未声明前使用值为undefined
  • 作用域为定义的代码所在函数内
// var 的情况
console.log(a); // 输出undefined
var a = 2;

http中301、302、307、308的区别

一、定义
301、308: 代表永久性转移Permanently Moved;
302、307:代表临时性转移Temporarily Moved;

二、区别
1、应用场景
1.1
301比较常用的场景是使用域名跳转。
比如,我们访问http://www.baidu.com 会跳转到https://www.baidu.com 发送请求之后,就会返回301状态码,然后返回一个location,提示新的地址浏览器就会拿着这个新的地址去访问。
  注意: 301请求是可以缓存的, 即通过看status code,可以发现后面写着from cache。
1.2
302 常用的场景是未登陆的用户访问需登录页重定向到登录页面、404页面重新定向到首页等。

三、区别

  • 302 重定向是临时的,搜索引擎会抓取新的内容和保留旧的地址。用户代理可能会在重定向后的请求中把POST 方法改为 GET 方法。

  • 301 重定向是永久的,搜索引擎会抓取新的内容和新的地址,同时,301请求可以被缓存。

  • 307 的定义实际上和 302 是一致的,唯一的区别在于,307状态码不允许浏览器将原本为POST的请求重定向到GET 请求上。

  • 308 的定义实际上和 301 是一致的,唯一的区别在于,308状态码不允许浏览器将原本为POST 的请求重定向到GET 请求上。

Http中的options请求的特性作用

一、主要用途
什么是options请求?options在http请求是一种特殊的请求,主要作用包括2个部分:

  • 获取服务器支持的HTTP请求方法
    options请求是用于请求服务器对于某些接口等资源的支持情况的,包括各种请求方法、头部的支持情况,仅作查询使用。
  • 用来检查服务器的性能
    例如:AJAX进行跨域请求时的预检,需要向另外一个域名的资源发送一个HTTP OPTIONS请求头,用以判断实际发送的请求是否安全。

为什么会用到options请求?
解决跨域问题的方法有很多种,CORS是比较好的解决方案,我们的项目也是用的这种模式,这个模式会有”预检”的请求,也就是正常请求之前的options请求。

Preflighted Requests是CORS中一种透明服务器验证机制。预检请求首先需要向另外一个域名的资源发送一个 HTTP OPTIONS 请求头,其目的就是为了判断实际发送的请求是否是安全的。

选项 是否允许 备注
Request has body No 没有请求体
Successful response has body No 成功的响应有响应体
Safe Yes 安全
Idempotent Yes 密等性,不变性,同一个接口请求多少次都一样
Cacheable No 不能缓存
Allowed in HTML forms No 不能在表单里使用

二、request header/response header
在浏览器里看到请求request header 和 response header的信息如下:
1、预检请求头request header的关键字段:

Request Header 作用
Access-Control-Request-Method 告诉服务器实际请求所使用的 HTTP 方法
Access-Control-Request-Headers 告诉服务器实际请求所携带的自定义首部字段,本次实际请求首部字段中content-type为自定义

服务器基于从预检请求头部获得的信息来判断,是否接受接下来的实际请求。

2、预检响应头response header的关键字段:

response header 作用
Access-Control-Allow-Methods 返回了服务端允许的请求,包含GET/HEAD/PUT/PATCH/POST/DELETE
Access-Control-Allow-Credentials 允许跨域携带cookie(跨域请求要携带cookie必须设置为true)
Access-Control-Allow-Origin 允许跨域请求的域名,这个可以在服务端配置一些信任的域名白名单
Access-Control-Request-Headers 客户端请求所携带的自定义首部字段content-type
Access-Control-Max-Age 预检请求的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被缓存多久
status-code 204 No Content

请求示例:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT 
Server: Apache/2.0.61 (Unix) 
Access-Control-Allow-Origin: http://foo.example 
Access-Control-Allow-Methods: POST, GET, OPTIONS 
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type 
Access-Control-Max-Age: 86400 
Vary: Accept-Encoding, Origin 
Content-Encoding: gzip 
Content-Length: 0 
Keep-Alive: timeout=2, max=100 
Connection: Keep-Alive 
Content-Type: text/plain

js中的==和===的区别

“==”叫做相等运算符,“===”叫做严格相等运算符。
"=="表示只要值相等即可为真,而"==="则要求不仅值相等,而且也要求类型相同。

'' == '0'          // false
0 == ''            // true
0 == '0'            // true
false == 'false'    // false
false == '0'        // true
false == undefined  // false
false == null      // false
null == undefined  // true
' \t\r\n ' == 0    // true
//undefined 和 null 与自身严格相等。
      
null === null  //true
undefined === undefined  //true      
undefined === null   //true
if (!undefined) {
  console.log('undefined is false');
}
// undefined is false

if (!null) {
  console.log('null is false');
}
// null is false

undefined == null  // true

NaN === NaN // false

vue中watch和computed区别

一、computed
vue中的computed是计算属性,根据依赖的属性动态显示新的计算结果,,该计算结果会被缓存起来。computed的值在getter执行后是会被缓存的。如果所依赖的数据发生改变时候, 就会重新调用getter来计算最新的结果。

 data: {
    firstName: 'Foo',
    lastName: 'Bar'
 },
 computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }

二、watch
vue中watch是监控侦听器,对data属性监控回调,但数据变化的时候自动触发回调函数,并传入传入newVal和oldVal两个参数。

data(){
    return {
        'first':{
          second:0
        }
    }
},
watch:{
    secondChange:{
        handler(oldVal,newVal){
          console.log(oldVal)
          console.log(newVal)
        },
        deep:true
    }
}

三、watch和computed区别

1、功能上:
computed是计算属性,依赖其它的属性计算所得出最后的值。watch是去监听一个值的变化,然后执行相对应的函数。

2、依赖收集的发生点:
watch 收集依赖的流程是发生在页面渲染之前,computed是在页面渲染时进行取值才会收集依赖。

3、使用上:
computed中的函数必须要用return返回;watch的回调里面会传入监听属性的新旧值,不是必须要用return返回。

4、性能上:
computed中的函数所依赖的属性没有发生变化,则直接从缓存中读取;而watch在每次监听的属性发生变化的时候都会执行回调。

5、使用场景
computed 擅长处理的场景:一个数据受多个数据影响,列如“”购物车商品结算“”;watch擅长处理的场景:一个数据影响多个数据,列如“”搜索框“”。

注: computed 的更新需要“渲染Watcher”的辅助,watch 不需要。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.