likes
comments
collection

【前端丛林】JavaScript这样服用,效果更佳(3)

作者站长头像
站长
· 阅读数 13

前言

哈喽大家好,我是Lotzinfly,一位前端小猎人。欢迎大家来到前端丛林,在这里你将会遇到各种各样的前端猎物,我希望可以把这些前端猎物统统拿下,嚼碎了服用,并成为自己身上的骨肉。今天是我们冒险的第三天,经过两天的考验相信大家已经有了足够的经验,那么今天我们会遇到什么样的猎物呢?话不多说,现在开启我们今天的前端丛林冒险之旅吧!

1. var和let

  • JS中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称ES6)中新增了块级作用域
  • 块作用域由 { } 包括, if 语句和 for 语句里面的 { } 也属于块作用域

1.1 ES5问题

1.1.1 全局变量

  • 在if或者for循环中声明的变量会变成全局变量
for(var i=0;i<=5;i++){
console.log("hello");
}
console.log(i); //5

1.1.2 内层变量可能会覆盖外层变量

var a = 1;
function fn() {
   console.log(a);
   if (false) {
      var a = 2;
    }
 }
fn(); //undefined

1.2 let

  • 允许块级作用域任意嵌套
  • 外层作用域无法读取内层作用域的变量
  • 内层作用域可以定义外层作用域的同名变量
  • 函数本身的作用域在其所在的块级作用域之内
'use strict'
function fn() {
    console.log("out");
}
(function () {
    if (false) {
      function fn() {
        console.log("in");
   }
}
   fn();
}());

1.3 var&let&const

  • var定义的变量没有块的概念,可以跨块访问,不能跨函数访问,有变量提升,可重复声明
  • let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问,无变量提升,不可以重复声明
  • let 声明的变量只在块级作用域内有效,不存在变量提升,而是绑定在暂时性死区
  • 或者说let变量提升了,但是在let声明变量前不能使用该变量,这特性叫暂时性死区(temporaldead zone)
  • 如果有重复变量 let 会在编译阶段报错

1.3.1 暂时性死区

// 不存在变量提升
'use strict';
function func(){
    console.log(i);
    let i;
 };
func(); // 报错

1.3.2 全局变量

  • ES5声明变量只有两种方式:var和function
  • ES6有let、const、import、class再加上ES5的var、function共有六种声明变量的方式
  • 浏览器环境中顶层对象是window,Node中是global对象
  • ES5中 顶层对象的属性等价于全局变量
  • ES6中var、function声明的全局变量,依然是顶层对象的属性;let、const、class声明的全局变量不属于顶层对象的属性

2. this

  • 当前函数的this是在被调用的时候才能确定的
  • 如果当前的执行上下文处于调用栈的栈顶,这个时候变量对象变成了活动对象,THIS指针才能确定

2.1 全局对象

  • 全局对象this指向本身
var a=1;//声明绑定变量对象,但在全局环境中,变量对象就是全局对象
this.b=2;//this绑定全局对象
c=3;//赋值操作 隐式绑定

2.1 用点调用

  • 在一个函数上下文中,this由函数的调用者提供,由调用函数的方式来决定指向
  • 如果是函数执行,如果前面有点,那么点前面是谁 this 就是谁
let obj = {
    getName(){
    console.log(this);
  }
};
obj.getName();

2.2 直接调用

  • 如果没有,this就是window(严格模式下是undefined),自执行函数中的this一般都是window
let obj = {
    getName(){
   console.log(this);
   }
};
let getName = obj.getName;
getName();

2.3 绑定事件

  • 给元素绑定事件的时候,绑定的方法中的this一般是元素本身
container.addEventListener('click',function(){
    console.log(this);
});

2.4 箭头函数

  • 箭头函数没有自己的this
  • 也没有prototype
  • 也没有arguments
  • 无法创建箭头函数的实例
let fn = () => {
    console.log(this);
    console.log(arguments);//Uncaught ReferenceError: arguments is not defined
 }
console.log(fn.prototype);//undefined
fn();
new fn();//VM4416:8 Uncaught TypeError: fn is not a constructor

2.5 构造函数

  • 构造函数中的THIS是当前类的实例
function fn(){}
let obj = new fn();

2.6 call/apply/bind

  • call/apply/bind可以改变函数中this的指向
  • 第一个参数是改变this指向(非严格模式下,传递null/undefined指向也是window)
  • call参数是依次传递,apply是以数组的方式传递
!function (proto) {
     function getContext(context) {
          context = context || window;
          var type = typeof context;
          if (['number', 'string', 'boolean', 'null'].includes(type)) {
              context = new context.constructor(context);
         }
         return context;
    }
     function call(context, ...args) {
         context = getContext(context);
         context._fn = this;
         let result = context._fn(...args);
         delete context._fn;
         return result;
   }
     function apply(context, args) {
          context = getContext(context);
          context._fn = this;
          let result = context._fn(...args);
          delete context._fn;
           return result;
 }
     function bind(context, ...bindArgs) {
          return (...args) => this.call(context, ...bindArgs, ...args);
}
     proto.call = call;
     proto.apply = apply;
     proto.bind = bind;
}(Function.prototype)

2.7绑定

  • 默认绑定
  • 隐式绑定
  • 显式绑定
  • new绑定
  • new > 显式 > 隐式 > 默认

隐式 > 默认

function one() {
    console.log(this)
}
var obj = {
   name: "obj",
   one
}
obj.one()

显式 > 隐式

function one() {
    console.log(this)
}
var obj = {
    name: "obj",
    one: one.bind("hello")
}
obj.one()

new > 显式

function one() {
    console.log(this)
}
var helloOne = one.bind("hello")
var obj = new helloOne();
console.log(obj);

结尾

好啦,这期的前端丛林大冒险先到这里啦!这期的猎物比较好对付,相信大家都可以拿下。希望大家可以好好品尝并消化,迅速升级,接下来我们将面对一个重量级猎物,大家要做好准备,我们下期再见。拜拜!