1.理解掌握闭包

一个函数对其周围状态(变量)的引用并保存周围变量,乃至函数执行完成后仍然可以访问的现象称之为闭包,闭包让外部访问函数内部的变量成为可能(私有属性),因为被引用的数据常驻内存,可能会造成内存泄露(一块内存长期被变量占据而不进行释放),闭包可以让你在一个内层函数中访问到其外层函数的作用域的状态并保存,在js中每当创建一个函数,闭包环境就在函数创建时产生,闭包环境相互独立,如果闭包环境引用外部函数作用域的一个变量a,即使当外部函数作用域的变量被销毁释放内存,但变量a不会被删除。

// 第一题
function funA(){
  var a = 10;  // funA的活动对象之中;
  return function(){   //匿名函数的活动对象;
        alert(a);
  }
}
var b = funA();
b();  //10

// 第二题

function outerFn(){
  var i = 0;
  function innerFn(){
      i++;
      console.log(i);
  }
  return innerFn;
}
var inner = outerFn();
inner();
inner();
inner();
var inner2 = outerFn();
inner2();
inner2();
inner2(); //1 2 3 1 2 3

// 第三题
var i = 0;
function outerFn(){
  function innnerFn(){
       i++;
       console.log(i);
  }
  return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();
inner2();
inner1();
inner2();     //1 2 3 4

// 第四题
function fn(){
    var a = 3;
    return function(){
        return  ++a;
    }
}
alert(fn()());  //4
alert(fn()());  //4

// 第五题
function outerFn(){
var i = 0;
  function innnerFn(){
      i++;
      console.log(i);
  }
  return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();
inner2();
inner1();
inner2();    //1 1 2 2

// 第六题
(function() {
  var m = 0;
  function getM() { return m; }
  function seta(val) { m = val; }
  window.g = getM;
  window.f = seta;
})();
f(100);
console.info(g());   //100  闭包找到的是同一地址中父级函数中对应变量最终的值

//第七题
function a() {
  var i = 0;
  function b() { alert(++i); }
  return b;
}
var c = a();
c();      //1
c();      //2

// 第八题
function f() {
  var count = 0;
  return  function() {
      count++;
      console.info(count);
  }
}
var t1 = f();
t1();     //1
t1();     //2
t1();     //3

// 第九题
var add = function(x) {
  var sum = 1;
  var tmp = function(x) {
      sum = sum + x;
      return tmp;
  }
  tmp.toString = function() {
      return sum;
  }
  return tmp;
}
alert(add(1)(2)(3));     //6

// 第十题
var lis = document.getElementsByTagName("li");
for(var i=0;i<lis.length;i++){
  (function(i){
      lis[i].onclick = function(){
           console.log(i);
      };
  })(i);       //事件处理函数中闭包的写法
}

// 第十一题
function m1(){
     var x = 1;
     return function(){
          console.log(++x);
     }
}

m1()();   //2
m1()();   //2
m1()();   //2

var m2 = m1();
m2();   //2
m2();   //3
m2();   //4

// 第十二
var  fn=(function(){
   var  i=10;
   function  fn(){
      console.log(++i);
   }
   return   fn;
})()
fn();   //11
fn();   //12

// 第十三
function love1(){
     var num = 223;
     var me1 = function() {
           console.log(num);
     }
     num++;
     return me1;
}
var loveme1 = love1();
loveme1();   //输出224

2.未声明和未定义的理解

  • 未声明的变量是程序中不存在且未声明的变量,访问未声明的变量会进行报错-未定义”或者“not definded";
  • 未定义的变量是在程序中声明但尚未给出任何值的变量,访问不会报错并得出undefined值。变量经过显示声明但是没有执行过赋值语句,那么它是“未定义”的,也就是确实意义上的"undefined"。我们可以使用它,但是它的默认值是"undefined"。

3.理解全局变量

声明变量时省略关键字就会造成变量污染。

4.定时器

延时一段时间并执行任务,因为它是宏任务,需要等待微任务执行完成后才可以执行,当设置一个等待时间为零的定时器它也不会立即执行!

5.相等符号”==“和”===“区别

  • ===称为严格等式运算符,两个操作数比较前不会进行类型转换,如果数据类型不同就直接定为不同;
  • ==包容等式运算符,两个操作数比较前如果发现数据类型不一样,则转换成数据相同的数据类型再比较,不同则不同;

6.隐式转换

逻辑运算符"=="进行两个是比较是否相等时,不同的数据类型会造成隐式转换后再比较,属于js中比较难的部分,具体情况如下:

  1. 数组和布尔值比较,[] == true;//false,空数组转为""再转为0,逻辑值true直接转为1;
  2. 数组符串比较,[1,2,3] == '1,2,3' // true,[1,2,3]转换成字符串"1,2,3"最后跟字符串比较;
  3. 字符串和数字进行比较,'1' == 1 // true,字符串转为数字后与数字1比较;
  4. 字符串和布尔值进行比较,'1' == true; // true,字符串转为数字1,布尔值转为数字1,比较相等;
  5. 布尔值和数字比较,true == 1;/ true,布尔值转为数字1再比较;
    有趣的事情是[] == false和![] == false的结果都是true,
    第一个[]数组转为""再转成0,false直接转为0,比较相等;
    第二个同理,!符号优先级高所以先执行,所以式子就变成(![]) == false,估计就能理解了!
    另外有几个比较的特殊undefined == null // true、NaN == XXX //NaN和任意类型都不等(包括自己)。

7.常见逻辑值假的值

js中空字符串""、NaN、undefined、null、flase,0,逻辑值为假,
但是空数组[]、空对象{}逻辑值为真。

8.基本数据类型

在ES5的时候,我们认知的数据类型确实是 6种:Number、String、Boolean、undefined、object、Null。
ES6 中新增了一种 Symbol 。这种类型的对象永不相等,即始创建的时候传入相同的值,可以解决属性名冲突的问题,做为标记。

基本类型(单类型):String、Number、boolean、null、undefined。
引用类型:object。里面包含的 function、Array、Date。

基础数据类型:undefined、String、、Symbol、Number、null、Booleanl;
复杂数据类型:数组、对象;

9.New操作符流程

1.创建了一个新的空对象{};
var obj = new Object()

2.设置新对象obj的proto = 构造函数.prototype;
obj.proto = 构造函数.prototype

3.构造函数中的this指向新对象a;
Var res = 构造器.call(obj),因为函数存在闭包所以每一个作用域都是相互独立,就有了对象的概念。

4.属性访问器中的setter中进行判断,如果是对象(引用类型则返回),否则返回空对象{};

10.防抖和节流
前端开发中我们经常需要绑定一些持续触发的函数,例如:跳转屏幕宽度resize,监听页面滚动scroll,拖拽mousemove。这些过程中绑定的函数频繁的触发,所以节流和防抖就很好的解决办法。

1.防抖debounce,指触发事件触发后的n秒内又被触发,则会重新计算时间,例如设置300毫秒的防抖时间,事件触发后的300毫秒内该事件又被触发,则重新计算时间,又将等待300毫秒。如果三百毫秒内没有触发,则直接执行事件的处理程序;

// 体验代码
<div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
<script>
    let num = 1;
    let content = document.getElementById('content');

    function count() {
        content.innerHTML = num++;
    };
    content.onmousemove = count;
</script>


function debounce(func, wait = 200) {
    let timeout; // 定义定时器
    return function () {
        let context = this; // 获取当前函数的指向者
        let args = arguments; // 获取到传递的参数对象
        if (timeout) clearTimeout(timeout); // 如果等待时间内又触发了事件,则将之间的定时器去掉,重新进行定时。

        timeout = setTimeout(() => {
            func.apply(context, args) // 将回调函数绑定给函数执行者并传入参数
        }, wait);
    }
}

2.节流throttle,连续触发事件,但是在n秒内只是执行一次处理程序,体现效果每200毫秒会执行事件的处理程序;

function throttle(func, wait = 200) {
  let timeout; // 定义定时器对象
  return function () {
    let context = this; // 获得函数执行对象
    let args = arguments; // 获得参数
    // 定时器为空时开启 不为空则还有定时任务 不执行处理函数
    if (!timeout) {
      // 开启定时任务
      timeout = setTimeout(() => {
        timeout = null; // 等待定时成功后将time赋值为空值
        func.apply(context, args);
      }, wait);
    }
  };
}

11.理解this指针

this简单的理解就是指向某一个作用域的指针,有以下几个场景与this指针相关:
1. 默认绑定,当函数调用时无任何调用前缀的情景,成为默认绑定此时this是指向全局对象window(非严格模式),严格模式下指向undefined,但是严格模式下调用不在严格模式中的函数是不会影响this指向;
2. 隐式绑定,函数调用者持有this,如果函数调用时存在前缀,那么this就会被绑定到前缀的对象上;

function doA() {
  console.log(this);
}
let obj = {
  name: "huasen",
  func: doA
}
obj.doA() // this指向obj(隐式绑定,函数调用者持有this)
  1. 显示绑定,通过call、apply、bind可以更改this的行为,更能感知this指向变化过程;
let obj1 = {
    name: 'call'
};
let obj2 = {
    name: 'apply'
};
let obj3 = {
    name: 'echo'
}
var name = '花森';
function fn() {
    console.log(this.name);
};
fn(); // 花森
fn.call(obj1); // call
fn.apply(obj2); // apply
fn.bind(obj3)(); // echo
  1. new操作符绑定
    通过构造函数new调用函数时,js以构造函数的prototype属性为原型创建一个新对象,this指向新对象,this和参数传递给构造器执行,构造器自动返回一个对象;
  2. 硬绑定
    箭头函数中this,不适用与上面介绍的绑定规则,准确来说箭头函数中的this指向当前作用域的父级作用域,一旦箭头函数的this绑定成功,将无法再次修改;

12.ES6新增特性

es6新特性详情点击
1. let和const;
2. 字符串拓展,include(是否包含子串)、startsWith(子串是否首部)等函数;
3. 解构表达式;
4. 箭头函数;
5. Promise;
6. set和map;
7. 模块化export和import模块化导入导出;
8. 对象拓展,keys,values方法;
9. find数组方法;

13.继承几种方式

// 原型链继承
Cat.prototype = new Animal();

// 构造继承,使用借用构造函数,通过call构造函数传入数据将父类的实例属性绑定到当前实例this。
function Cat(name){
  Animal.call(this); // 可以传入数据
  this.name = name || 'Tom';
}

// 遍历拷贝继承,遍历父类的属性并赋值给子类的原型对象,注意深浅拷贝问题。
function Cat(name){
  var animal = new Animal();
  for(var key in animal){
    Cat.prototype[key] = animal[key];
  }
  this.name = name || 'Tom';
}
// 寄生组合继承
function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
(function(){
  // 创建一个没有实例方法的类
  var Super = function(){};
  Super.prototype = Animal.prototype;
  //将实例作为子类的原型
  Cat.prototype = new Super();
})();

// class继承
class Cat extends Animal {
  constructor() {

  }
}

14.处理异步任务方式

//1.回调函数(callback)
function A(callback){
    console.log("I am A");
    callback();  // 传入函数
}
//2.Promise
new Promise((resolve,reject) => {
  resolve("成功处理");
})
//3.订阅者发布者模式

15.null和undefined区别

null代表空值代表空对象指针,没有对象此处不应该有值,对象原型链的终点;
undefined代表声明但未赋值的变量,undefined是一个预设的全局变量。

typeof undefined //  undefined
typeof null // object
null == undefined // true
undefined + 6 // NaN
null + 6 // 6

16.数组去重

// ES6利用set去重
Array.from(new Set(arr))

// for嵌套for+splice截掉去重
for() {
  for() {
    if(arr[i] == arr[j]) {
      arr.splice(j,1);
      j--;
    }
  }
}

// indexOf去重
arr.map((item)=> {
  if(arry.indexOf(item) == -1) {
    array.push(item);
  }
})

17.原型链

由构造函数prototype原型对象通过proto连接组成的一条指针链路就指原型链

18.require 和 import

require是AMD的引入方法,require是运行时调用,引入资源将赋值给某一个变量;import是ES6的语法标准,编译时调用所以必须放在文件开头,import引入资源是属于结构过程。

19.常见HTTP请求

  1. get;
  2. put;
  3. post;
  4. delete;
  5. head;

19.同源策略

http协议中规定同协议、同域名、同端口简称为同源策略,同源策略是为了互联网的安全,例如:网站A是无法访问到网站B所存入的cookie信息,因为不符合同源策略的标准。

20.常见跨域方式

  1. jsonp;
  2. document.domain,二级域名和顶级域名的资源共享,两个页面设置同样的document.domain属性可以实现跨域;
  3. window.name,页面中打开页面后window.name任然存在且可以访问;
  4. postMassage,html5新增接口,多窗口中的信息传递;
  5. 跨域资源共享CORS,服务器端Access-Control-Allow-Origin设置可以通过的源,前端页面如果需要接收服务器带回的Cookie信息,需要打开xhr.withCredentials = true;确定请求是否携带Cookie;
  6. nginx反向代理;

21.常见设计模式

  1. 工厂模式;
  2. 观察者模式;
  3. 混合模式;

22.禁止事件冒泡

事件冒泡现象点击点击子元素,父元素的click事件同样会触发,使用event.stopPropagation()阻止冒泡。

23.阻止默认事件

存在href属性的a标签被点击后,默认会跳转页面,form表单中类型为submint的input框被点击后默认提交表单,这一个现象称为默认行为,可以通过event.preventDefault()阻止。

24.标签a伪类设置顺序

排列顺序:L-V-H-A : a:link {} a:visited {} a:hover {} a:active {}

25.标签a禁止跳转

e.preventDefault()和href="javascript:void(0)"可以实现

26.中断ajax请求方式

  1. 请求时间超时自动停止ajax请求;
  2. 调用XML对象的abort方法进行手动停止;

27.事件代理理解

事件委托是指将事件绑定到目标元素的父元素上,利用事件的冒泡机制触事件;好处是节省内存的开销以及对新增加的元素节点同样生效。

28.事件属性target和currentTarget理解

  1. target被点击触发事件的元素;
  2. currentTarget捕获到到并执行程序的元素;

29.宏任务和微任务理解

执行顺序是同步代码>微任务>宏任务,程序执行,限制执行同步代码,遇到微任务(resolve())进入微任务队列中,优先执行微任务,遇到宏任务进入函数调用栈中的指向的任务成为宏任务,按顺序弹出执行;一次CPN轮询下,先执行同步代码,将微任务队列中的任务全部执行完成,再按顺序执行函数调用栈中的宏任务。

30.get和post请求区别

  1. 传参数的方式不同,get是通过URL明文传参,post请求参数不可见,存在与http的请求体;
  2. 传递的大小不同,get请求参数拼接在url后面,传递长度受限制,post请求传递的长度没有限制;
  3. 编码方式不同,get只是支持URL编码和ASCLL字符编码,post可以进行多种编码方式;
  4. 健壮性不同,get请求回退不会重新发送,post请求回退后重新提交表单;

31.常见响应状态码

浏览器输入网址打开网页时,浏览器会向服务器发送请求后会收到服务器返回的一个响应报文,报文中的存在有HTTP状态码的头信息。浏览器通过状态码进行一系列操作,状态码反应当前请求的状态。

常见状态码

  • 200正常请求;
  • 301资源(网页等)被永久转移到其它URL;
  • 404请求的资源(网页等)不存在;
  • 500内部服务器错误;

状态码分类

分类 分类描述
1** 信息提示(服务器收到请求但需要请求者继续执行操作)
2** 成功(操作被成功接收并处理)
3** 重定向(需要进一步的操作以完成请求)
4** 客户端错误(请求包含语法错误或无法完成请求)
5** 服务器错误(服务器在处理请求的过程中发生了错误)