前端知识散记

闭包

什么是闭包?

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)

关键词:函数,周围状态引用,捆绑,引用包围,组合

闭包的用途

闭包让你可以在一个内层函数访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。

function init(){                //外层函数,引用包围开始
    let name = 'jack'
    function getName(){         //内层函数
     console.log(name)          // 访问外层函数的变量
    }
    getName()                    
}                               //引用包围结束

init()
复制代码

闭包的优点(使用闭包的原因)

  1. 模拟私有化方法

  2. 将函数与其所操作的某些数据(环境)关联起来

var Counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
})();

console.log(Counter.value()); /* logs 0 */
Counter.increment();
Counter.increment();
console.log(Counter.value()); /* logs 2 */
Counter.decrement();
console.log(Counter.value()); /* logs 1 */

复制代码

Counter.incrementCounter.decrementCounter.value三个函数所共享词法环境。三个函数只可以通过Counter访问,三个函数和privateCounter关联

闭包的缺点

  1. 闭包在处理速度和内存消耗方面对脚本性能具有负面影响。

  2. 循环中创建闭包中,可能会出现直接取到循环最后的值(在for循环中使用 var 定义变量例子

call()、apply()、bind() 的用法分别是什么?

  1. call() 方法允许为不同的对象分配和调用属于一个对象的函数/方法。

    • 语法

    function.call(thisArg, arg1, arg2, …)

    • 用 call方法调用父构造函数
    function Product(name, price) {
      this.name = name;
      this.price = price;
      console.log()
    }
    
    function Food(name, price) {
      Product.call(this, name, price);
      this.category = 'food';
    }
    
    function Toy(name, price) {
      Product.call(this, name, price);
      this.category = 'toy';
    }
    
    var cheese = new Food('feta', 5).name;  // expected output: "feta"
    var fun = new Toy('robot', 40).price;  // expected output: 40
    //Food 和 Toy 都有了 this.name = name;this price = price
    复制代码
    • 使用 call 方法调用匿名函数
    var animals = [
      { species: 'Lion', name: 'King' },
      { species: 'Whale', name: 'Fail' }
        ];
    
    for (var i = 0; i < animals.length; i++) {
      (function(i) {
        this.print = function() {
          console.log('#' + i + ' ' + this.species
                      + ': ' + this.name);
        }
        this.print();
      }).call(animals[i], i);
    }
    复制代码

    这个匿名函数的主要目的是给每个数组元素对象添加一个 print 方法,这个 print 方法可以打印出各元素在数组中的正确索引号。

    • 使用 call 方法调用函数并且指定上下文的 ‘this’
    function greet() {
      var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
      console.log(reply);
    }
    
    var obj = {
      animal: 'cats', sleepDuration: '12 and 16 hours'
    };
    
    greet.call(obj);  // cats typically sleep between 12 and 16 hours
    
    复制代码

    注意:当使用 call 方法调用调用函数并且不指定第一个参数(argument),this的值将会被绑定为全局对象。

  2. 使用apply()方法, 你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。

    • 语法

    func.apply(thisArg, [argsArray])

    • 对于一些需要写循环以便历数组各项的需求,我们可以用apply完成以避免循环。

      我们可以使用push将元素追加到数组中。由于push接受可变数量的参数,所以也可以一次追加多个元素。

      但是,如果push的参数是数组,它会将该数组作为单个元素添加,而不是将这个数组内的每个元素添加进去,因此我们最终会得到一个数组内的数组。如果不想这样呢?concat符合我们的需求,但它并不是将元素添加到现有数组,而是创建并返回一个新数组。 然而我们需要将元素追加到现有数组……那么怎么做好?难道要写一个循环吗?别当然不是!

      var array = ['a', 'b'];
      var elements = [0, 1, 2];
      array.push.apply(array, elements);
      console.info(array); // ["a", "b", 0, 1, 2]
      复制代码

注意:call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。

  1. bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用

当需要调用一个函数内部的函数(内部函数访问了其外部函数的变量),我们需要最终结果内部函数仍可以访问外部函数的变量,但此时内部函数已经剥离出外部函数,就需要使用bind()方法,将外部函数作为参数传入到内部函数中。

    this.x = 9;    // 在浏览器中,this 指向全局的 "window" 对象
    var module = {
      x: 81,
      getX: function() { return this.x; }
    };

    module.getX(); // 81

    var retrieveX = module.getX;
    //等同于
    var retrieveX = function () { return this.x }  
    
    retrieveX();
    // 返回 9 - 因为函数是在全局作用域中调用的

    // 创建一个新函数,把 'this' 绑定到 module 对象
    // 新手可能会将全局变量 x 与 module 的属性 x 混淆
    var boundGetX = retrieveX.bind(module);     //重新进行关联
    boundGetX(); // 81
复制代码

常见HTTP状态码

  • 2xx 成功,操作被成功接收并处理
    • 200 OK 请求成功。成功的含义取决于HTTP方法。

    • 204 No Content 服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息。

    • 206 Partial Content 服务器已经成功处理了部分 GET 请求。(断点续传或者分段下载)

  • 3xx 重定向,需要进一步的操作以完成请求
    • 301 Moved Permanently 请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替

    • 302 Found 请求的资源现在临时从不同的 URI 响应请求。

    • 303 See Other 对应当前请求的响应可以在另一个 URI 上被找到,而且客户端应当采用 GET 的方式访问那个资源。

    • 304 Not Modified 如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。

  • 4xx 客户端错误,请求包含语法错误或无法完成请求
    • 400 Bad Request 语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。请求参数有误。

    • 401 Unauthorized 当前请求需要用户验证。该响应必须包含一个适用于被请求资源的 WWW-Authenticate 信息头用以询问用户信息。

    • 403 Forbidden 服务器已经理解请求,但是拒绝执行它。

    • 404 Not Found 请求失败,请求所希望得到的资源未被在服务器上发现。

    • 405 Method Not Allowed 请求行中指定的请求方法不能被用于请求相应的资源。

  • 5xx 服务器错误,服务器在处理请求的过程中发生了错误
    • 500 Internal Server Error 服务器遇到了不知道如何处理的情况。

    • 501 Not Implemented 此请求方法不被服务器支持且无法被处理。只有GETHEAD是要求服务器支持的,它们必定不会返回此错误代码。

    • 502 Bad Gateway 此错误响应表明服务器作为网关需要得到一个处理这个请求的响应,但是得到一个错误的响应。

    • 503 Service Unavailable 服务器没有准备好处理请求。 常见原因是服务器因维护或重载而停机。 请注意,与此响应一起,应发送解释问题的用户友好页面。

数组去重

array = [1,5,2,3,4,2,3,1,3,4]
//方法一
function unique(array) {
   return [...new Set(array)];
}
//方法一的简化
let unique = (a) => [...new Set(a)]

//方法2
let temp_arr = []
function unique(arr){
  arr.map(item =>{
       if( temp_arr.indexOf(item) === -1){
           temp_arr.push(item)
       }
   })
   return temp_arr
}


复制代码

缺陷:当数组中存在NaN时,方法2就存在漏洞,因为Array.indexOf使用的是 === 来进行判断,众所周知 NaN === NaN 返回false

事件委托、阻止默认事件、阻止事件冒泡

什么是事件委托?

由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理\委托(delegation)。

阻止默认事件

event.preventDefault();
复制代码

阻止事件冒泡

event.stopPropagation();
复制代码

JS中的继承

原型链继承

function SuperType() {
    this.property = true;
}

SuperType.prototype.getSuperValue = function() {
    return this.property;
}

function SubType() {
    this.subproperty = false;
}

// 这里是关键,创建SuperType的实例,并将该实例赋值给SubType.prototype
SubType.prototype = new SuperType(); 

SubType.prototype.getSubValue = function() {
    return this.subproperty;
}

var instance = new SubType();
console.log(instance.getSuperValue()); // true

复制代码

class 继承

class Rectangle{
    constructor(width, height){
        this.width = width
        this.height = height
    }
    area (){
        return this.width*this.height
    }
}
class Square extends Rectangle{
    constructor(length){
        super(length,length)
        this.name = 'square'
    }
     // 如果子类中存在构造函数,则需要在使用“this”之前首先调用 super()。
}
const rectangle = new Rectangle(10, 20);
console.log(rectangle.area());  //200

const square = new Square(10);
console.log(square.area());  //100
复制代码

数组排序

function sort(numbers){
    if ( numbers.length > 2 ){
        let tempMin = min(numbers)
        let indexMin = numbers.indexOf(tempMin)
        numbers.splice(indexMin,1)
        return [tempMin].concat(sort (numbers) )
    } else{
        return numbers[0] < numbers[1] ? numbers : numbers.reverse
    }
}

function min (numbers) {
    if ( numbers.length > 2){
        return min( 
            [ numbers[0], min( numbers.slice(1) ) ] 
            )
    }
    else {
        return Math.min.apply(null, numbers)
    }
 }

 console.log(sort( [2,1,5,3,8,4,9,5]) )
//[1,2,3,4,5,5,8,9]
复制代码

Promise

Preomise 是一个对象,它代表着一个异步操作最终的成功或者失败,并有效解决回调地狱

创建一个 new Promise

想要某个函数拥有promise功能,只需让其返回一个promise即可。

function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    ....
  });
};
复制代码

Promise.prototype.then()

then() 方法返回一个 Promise 。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。

var p1 = new Promise((resolve, reject) => {
  resolve('成功!');
  // or
  // reject(new Error("出错了!"));
});

p1.then(value => {
  console.log(value); // 成功!
}, reason => {
  console.error(reason); // 出错了!
});

复制代码

Promise.all()

Promise.all 方法接收一个可iterablepromise(注:Array,Map,Set都属于ES6的iterable类型)输入,并且只返回一个Promise实例,那个输入的所有promiseresolve回调的结果是一个数组。这个Promiseresolve回调执行是在所有输入的promiseresolve回调都结束,或者输入的iterable里没有promise了的时候。它的reject回调执行是,只要任何一个输入的promisereject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出的错误信息。(来源:MDN)

太长了,总结下:

Promise.all()方法接收一个可迭代(Array,Map,Set都属于ES6的iterable类型)的preomise,返回一个Premise实例,输入的所有promise的resolve回调的结果组成了一个数组,返回的Premise实例当输入的所有promise的resolve回调结束才resolve,遇到输入的promise出现reject,就立即抛出的错误信息。

都是resolve的情况

var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([p1, p2, p3]).then(values => {
  console.log(values); // [3, 1337, "foo"]
});
复制代码

含有reject的情况

var p1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'three');
});
var p4 = new Promise((resolve, reject) => {
  setTimeout(resolve, 4000, 'four');
});
var p5 = new Promise((resolve, reject) => {
  reject('reject');
});

Promise.all([p1, p2, p3, p4, p5]).then(values => {
  console.log(values);
}, reason => {
  console.log(reason)
});

//From console:
//"reject"

//You can also use .catch
Promise.all([p1, p2, p3, p4, p5]).then(values => {
  console.log(values);
}).catch(reason => {
  console.log(reason)
});

//From console:
//"reject"


复制代码

Promise.race()

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

简单来说就是第一状态传递

var p1 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 500, "one");
});
var p2 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 100, "two");
});

Promise.race([p1, p2]).then(function(value) {
  console.log(value); // "two"
  // 两个都完成,但 p2 更快
});

var p3 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 100, "three");
});
var p4 = new Promise(function(resolve, reject) {
    setTimeout(reject, 500, "four");
});

Promise.race([p3, p4]).then(function(value) {
  console.log(value); // "three"
  // p3 更快,所以它完成了
}, function(reason) {
  // 未被调用
});

var p5 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 500, "five");
});
var p6 = new Promise(function(resolve, reject) {
    setTimeout(reject, 100, "six");
});

Promise.race([p5, p6]).then(function(value) {
  // 未被调用
}, function(reason) {
  console.log(reason); // "six"
  // p6 更快,所以它失败了
});


复制代码

跨域

同源政策

同源= 协议+域名+端口

同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。

什么是跨域

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

JSONP 跨域

利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。
JSONP只支持GET请求,JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

//请求端
<script src="http://localhost:9090/api"></script>

//服务端
const http = require('http');
http.createServer((req, res) => {
    if (req.url === '/api') {
        ....
    }
})
.listen(9090, () => {
    console.log(9090)
})

复制代码

CORS 跨域

通过设置响应头来解决:Access-Control-Allow-Origin:


//服务端
const http = require('http');
http.createServer((req, res) => {
    if (req.url === '/api') {
        response.statusCode = 200
        response.setHeader("Access-Control-Allow-Origin", "http://xxxx.com");
    }
})
.listen(9090, () => {
    console.log(9090)
})

复制代码

每日一语:时间会风化你的意志,用自我肯定去维护它。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享