系统化学习前端之JavaScript(ES5)

发布时间 2023-03-30 17:13:13作者: 深巷酒

前言

ES5 是 ECMAScript 的拓展,发布于 2009 年,目前大多数浏览器已支持,只有少部分不再维护低版本浏览器不支持,如 IE6 等。

ES5

ES5 是 ECMAScript 更新的 5.0 版本,更新内容多为代码规范与安全相关。

严格模式

严格模式是指对当前 js 文件规定一系列要求,不满足这些要求,js 文件执行会抛出错误。

严格模式要求:

  1. 禁止给未声明变量赋值。

  2. 禁止删除变量和函数。

  3. 禁止函数参数命名重复。

  4. 禁止使用八进制整型。

  5. 禁止使用 eval()with() {} 破坏作用域的方法。

  6. 禁止使用 argumentsargumens.callee

    常规模式下,arguments 是函数参数组成的类数组对象,argumens.callee 可以替换函数名。

  7. 禁止使用预留关键字命名变量。如 implements, interface, package, private, public, static, yield, let, package。

在 js 文件的开头添加 'use strict' 可以开启当前文件的严格模式。

this 关键字

var obj = {
    name: 'jason',
    getName: function () {
        return this.name
    }
}

对象方法中想要使用对象属性,可以使用 this 关键字替代对象,通常称:this 指向 obj。由此类推,this 用作函数内,指向调用函数的对象。简而言之,this 谁调用指向谁

  1. 不同函数的 this 指向

    • 普通函数

      function fun() {
          console.log(this) // this 指向 window
      }
      fun()
      

      注意:严格模式下,普通函数(包含匿名函数)的 this 指向 undefined

    • 构造函数

      function Fun(name) {
          this.name = name
          console.log(this) // this 指向 obj
      }
      
      var obj = new Fun('jason')
      
    • 原型方法

      function Fun(name) {
          this.name = name
      }
      
      Fun.prototype.run = function() {
          console.log(this) // this 指向 obj
      }
      
      var obj = new Fun('jason')
      obj.run()
      
    • 对象方法

      var obj = {
          name: 'jason',
          getName: function () {
              console.log(this) // this 指向 obj
          }
      }
      obj.getName()
      
    • 事件回调函数

      var app = document.getElementById('app')
      
      function handleClick() {
          console.log(this) // this 指向 app
      }
      
      app.addEventListener('click', handleClick)
      
  2. 更改函数的 this 指向

    • call

      function sum(x, y) {
          console.log(this) // this 指向 由 window 更改为 obj
          return x + y
      }
      
      var obj = { name: 'jason' }
      
      sum.call(obj, 2, 3) // 5
      
    • apply

      function sum(x, y) {
          console.log(this) // this 指向 由 window 更改为 obj
          return x + y
      }
      
      var obj = { name: 'jason' }
      
      sum.apply(obj, [2, 3]) // 5
      
    • bind

      function sum(x, y) {
          console.log(this) // this 指向 由 window 更改为 obj
          return x + y
      }
      
      var obj = { name: 'jason' }
      
      var newSum = sum.bind(obj)
      newSum(2,3) // 5
      

注意:call, apply, bind 为函数的方法。call, apply执行并调用函数,调用过程中改变 this 指向,bind 执行返回新函数,返回的新函数改变了 this 指向。

对象属性的特性

特性用于描述对象属性的特征,特性是为了实现 JavaScript 引擎使用的,JavaScript 中不能直接访问。根据特性不同,对象属性可以划分为数据属性和访问器属性。

  1. 数据属性

    数据属性具有 4 个描述特征的特性:

    • Configurable

      表示能否通过 delete 删除属性,或者能否修改属性的特性,或者能否把属性修改为访问器属性,默认特性值为 true。

    • Enumerable

      表示能否通过 for-in 循环返回属性,默认特性值为 true。

    • Writable

      表示能否修改属性的值,默认特性值为 true。

    • Value

      属性的值,默认为 undefined。

    var obj = {
        name: 'jason'
    }
    

    字面量定义对象,name 属性的 Value 特性值为 jason,其他三个特性的特性值均为 true。

    Object.defineProperty() 可以修改属性的特性,接收三个参数,分别为对象,属性以及特性对象。

    var obj = {
        name: 'jason'
    }
    
    Object.defineProperty(obj, 'name', {
    	value: 'jim'
    })
    
    	obj.name = 'jack'
    	console.log(obj.name) // jim
    
    	Object.defineProperty(obj, 'name', {
    	    configurable: false, // 一旦设置为 false,不可再修改为 true
    	    value: 'jim'
    	})
    
    	delete obj.name // 严格模式 抛出错误
    	console.log(obj.name) // 常规模式 jim
    
  2. 访问器属性

    字面量方法定义对象,对象属性为数据属性,无法定义对象属性为访问器属性,只能通过 Object.defineProperty()定义。访问器属性的 4 个特性:

    • Configurable

      表示能否通过 delete 删除属性,或者能否修改属性的特性,或者能否把属性修改为数据属性,默认特性值为 true。

    • Enumerable

      表示能否通过 for-in 循环返回属性,默认特性值为 true。

    • Get

      在读取属性时调用的函数,默认值为 undefined。

    • Set

      在写入属性时调用的函数,默认值为 undefined。

    var obj = {
        name: 'jason',
        age: 20
    }
    
    Object.defineProperty(obj, 'age', {
        get: function () {
            return this._age  // _age 为 属性 age 的备份,防止在 set 过程中引发循环调用,导致栈溢出。
        },
        set: function (val) {
            this._age = val
            if(this._age > 20) {
                this.name = 'jim'
            }
        }
    })
    
    obj.age = 21
    console.log(obj.age) // 21
    console.log(obj.name) // jim
    

    通过 Object.defineProperty()obj 数据劫持,其属性改造为访问器属性,访问器属性可以做到修改单个属性,其他属性跟随变化的特点。

    注意:setget 不设置会导致属性不可写或不可读。

  3. 批量设置数据属性和访问器属性

    通过 Object.defineProperties() 可以批量设置属性为数据属性或者访问器属性,接收两个参数:对象以及属性对象。

    Object.defineProperties(obj, {
        name: {
            value: 'jason'
        },
        age: {
            get: function () {
                return this._age
            },
            set: function (val) {
                his._age = val
            }
        }
    })
    
    obj.age = 21
    console.log(obj) // {_age: 21, name: 'jason'}
    console.log(obj.age) // 21
    

对象属性保护

对象的属性可以进行增删改查操作,因此对象是可变的。

  • 增:obj.newAttr = val;

  • 删:delete obj.attr;

  • 改:obj.attr = val;

  • 查:obj.attr;

两种方式可以保护对象的属性,防止篡改。

  1. 使用 Object.defineProperty() 修改属性的特性值,使其不可更改。

    Object.defineProperty(obj, 'attr', {
        configurable: false,
        writable: false,
        value: ''
    })
    
  2. 使用 Object 静态方法

    • Object.preventExtensions(obj);

      防拓展,禁止增。其原理是将对象中的隐藏属性 extensible,由默认的 true 设置为 false。

      注意:可以通过 Object.isExtensible(obj) 查看是否可拓展,返回 boolean 类型。

    • Object.seal(obj);

      封闭,禁止增、删。其原理是将对象中的 extensible 设置 false,并将属性的特性 configurable 设置为 false。

      注意:可以通过 Object.isSealed(obj) 查看是否封闭,返回 boolean 类型。

    • Object.freeze(obj);

      冻结,禁止增、删、改。其原理是将对象中的 extensible 设置 false,并将属性的特性 configurable 和 writable 设置为 false。

      注意:可以通过 Object.isFrozen(obj) 查看是否冻结,返回 boolean 类型。

原子继承

Object.create() 可以子对象继承父对象,同时为子对象添加数据属性或者访问器属性。

var father = {
    eyes: 'black'
}

var child = Object.create(father, {
    name: {
        configurable: true,
        enumerable: true,
        writable: true,
        value: 'jason',
    },
    age: {
        get: function () {
            return this._age
        },
        set: function (val) {
            this._age = val
        }
    }
})

child.age = 21
console.log(child) // {name: 'jason', _age: 21}
console.log(child.eyes) // black

后记

当然 ES5 还新增了一些 string 和 array 的方法,在 ECMAScript 的内置对象中已经有所添加,可参考 ECMAScript