with 语句
with 语句可以形成自己的作用域,严格模式下不允许使用 "with" 语句。
with 语句扩展一个语句的作用域链。
不建议使用 with 语句,因为它可能是混淆错误和兼容性问题的根源。
js
var message = "Hello World"
var obj = {
name: 'why',
age: 18,
message: 'kaimo313'
}
function foo() {
console.log('foo---->', message)
function bar() {
with(obj) {
console.log('bar---->', message)
}
}
bar()
}
foo()
eval 函数
eval 是一个特殊的函数,它可以将传入的字符串作为 JavaScript 代码来执行。
js
var jsString = "var message = 'Hello World'; console.log(message)";
eval(jsString);
console.log(message);
不建议在开发中使用 eval 函数
- eval 代码的可读性非常差
- eval 是一个字符串,可能在执行的过程中被刻意篡改,可能造成被攻击的风险
- eval 的执行必须经过 js 解释器,不能被 js 引擎优化
认识严格模式
在 ECMAScript5 中引入了严格模式的概念(Strict Mode):
严格模式很好理解,是一种具有限制性的 JavaScript 模式,从而使代码隐式的脱离了'懒散(sloppy)模式'。
严格模式对正常的 JavaScript 语义进行了一些限制:
- 严格模式通过抛出错误来消除一些原有的
静默(silent)错误 - 严格模式让 JS 引擎在执行代码时可以进行更多的优化(不需要对一些特殊的语法进行处理)
- 严格模式禁用了在 ECMAScript 未来版本中可能会定义的一些语法
静默(silent)错误:不报错也没有任何效果
js
123.name = 'abc'
var obj = {}
Object.defineProperty(obj, 'name', {
writable: false
})
obj.name = 'why'
开启严格模式
严格模式通过在文件或者函数开头使用 "use strict" 来开启。
给文件跟函数开启严格模式:
js
"use strict"
message = "Hello World"
console.log(message)
true.foo = 'bar'
function foo() {
"use strict"
true.foo = 'bar'
}
foo()
严格模式限制
几个严格模式模式下的严格语法限制:
- 无法意外的创建全局变量
- 严格模式会使引起静默失败的赋值操作抛出异常
- 严格模式下试图删除不可删除的属性
- 严格模式不允许函数参数有相同的名称
- 不允许0的八进制语法
- 在严格模式下,不允许使用 with
- 在严格模式下,eval 不再为上层引用变量
- 严格模式下,this 绑定不会默认转成对象
js
// 意外的创建全局变量
message = "Hello World"
console.log(message)
function foo() {
age = 20
}
foo()
// 不允许函数参数有相同的名称
function bar(age, age) {
console.log(age, age)
}
bar(18, 20)
// 静默错误
true.foo = 'bar'
NaN = 123
// 试图删除不可删除的属性
var obj = {}
Object.defineProperty(obj, 'name', {
configurable: false,
value: 'why'
})
delete obj.name
// 不允许0的八进制语法
var num = 0123 // "0o123"
console.log(num)
// eval 不再为上层引用变量
var jsString = "var message = 'Hello World'; console.log(message)";
eval(jsString);
console.log(message);
在严格模式下,自执行函数(默认绑定)会指向 undefined
js
'use strict'
function foo() {
console.log(this) // undefined
}
var obj = {
name: 'why',
foo: foo
}
foo()
var bar = obj.foo
bar()
setTimeout(() => {
console.log(this) // Window
}, 1000)
// chromium 浏览器 setTimeout 的实现
// fakeWin.setTimeout = function(fn, time) {
// fakeWin.setTimeout.called = true
// fakeWin.setTimeout.that = this
// if(typeof fn === 'string') {
// eval(fn)
// } else {
// fn.apply(this, Array.prototype.slice.call(arguments, 2))
// }
// }
setTimeout(function () {
console.log(this) // Window
}, 1000)
面向对象是现实的抽象方式
对象是 JavaScript 中一个非常重要的概念,这是因为对象可以将多个相关联的数据封装在一起,更好的描述一个事物。
比如我们可以描述一辆车:Car,具有颜色、速度、品牌、价格、行驶等等
比如我们可以描述一个人:Person,具有姓名、年龄、性别、身高、吃东西、跑步等等
用对象来描述事物,更有利于我们将现实的事物,抽离成代码中某个数据结构:
- 所以有一些编程语言就是纯面向独享的编程语言,比如 Java;
- 你在实现任何现实抽象时都需要先创建一个类,根据类再去创建对象
JavaScript 的面向对象
JavaScript 其实支持很多种编程范式的,包括函数式编程和面向对象编程。
JavaScript 中的对象被设计成一组属性的无序集合,像是一个哈希表,有key和value组成
key是一个标识符名称,value可以是任意类型,也可以是其他对象或者函数类型
如果value是一个函数,那么我们称之为对象的方法。
如何创建一个对象?
- 早期使用创建对象的方式最多的是使用 Object 类,并且使用 new 关键字来创建一个对象(早期java开发者习惯于通过 new 来创建)
- 通过字面量的形式来创建对象(简洁、内聚性强)
js
// 创建一个对象,对某一个人进行抽象
var obj = new Object()
obj.name = 'why'
obj.age = 18
obj.sex = 'male'
obj.height = 1.8
obj.eat = function () {
console.log('eat')
}
// 创建方式2:使用字面量方式
var obj2 = {
name: 'why',
age: 18,
sex: 'male',
height: 1.8,
eat: function () {
console.log('eat')
}
}
对对象属性的操作
如果我们想要对一个属性进行比较精确的操作控制,那么我们就可以使用属性描述符。
通过属性描述符可以精准的添加或修改对象的属性。
属性描述需要使用 Object.defineProperty 来对属性进行添加或者修改。
js
var obj = {
name: 'why',
age: 18
}
// 获取属性
console.log(obj.name)
// 给属性赋值
obj.name = 'kaimo'
console.log(obj.name)
// 删除属性
delete obj.name
console.log(obj)
// 对属性操作时进行一些限制
// 1. 不允许某一个属性被赋值
// 2. 不允许某个属性删除
// 3. 不允许某些属性在遍历对象时被遍历出来
Object.defineProperty(obj, 'height', {
value: 1.8
})
Object.defineProperty
Object.defineProperty(obj, prop, descriptor) 方法会直接在一个对象上定义一个新属性,或者修改一个已有的属性,并返回该对象。
obj:要定义属性的对象prop:要定义的属性名descriptor:属性描述符对象
属性描述符分类
属性描述符可以分为二种类型
- 数据属性描述符(Data Properties Descriptor)
- 存取属性描述符(Accessor Properties Descriptor)
| configurable(属性是否可配置) | enumerable(属性是否可枚举) | value(属性值) | writable(属性是否可写) | get(获取属性值) | set(设置属性值) | |
|---|---|---|---|---|---|---|
| 数据属性描述符 | 可以 | 可以 | 可以 | 可以 | 不可以 | 不可以 |
| 存取属性描述符 | 可以 | 可以 | 不可以 | 不可以 | 可以 | 可以 |
数据属性描述符
数据属性描述符有如下四个特性:
[[configurable]]:表示属性是否可以通过 delete 删除属性,是否可以修改它的的特性,或者是否可以将它修改为存取属性描述符- 直接在一个对象上定义某个属性时,这个属性的 configurable 值为 true
- 通过属性描述符定义一个属性时, configurable 值为 false
[[enumerable]]:表示属性是否可以通过for-in或者Object.keys()返回该属性- 直接在一个对象上定义某个属性时,这个属性的 enumerable 值为 true
- 通过属性描述符定义一个属性时, enumerable 值为 false
[[writable]]:表示是否可以修改属性的值- 直接在一个对象上定义某个属性时,这个属性的 writable 值为 true
- 通过属性描述符定义一个属性时, writable 值为 false
[[value]]:属性的 value 值,读取属性时返回该值,修改属性时,会对其进行修改- 默认情况下这个值是 undefined
js
// name 跟 age 没有使用属性描述符定义,但是它们也是具备对应的特性的
// 以 name 属性为例
// value: 'why'
// configurable: true
// enumerable: true
// writable: true
var obj = {
name: 'why',
age: 18
}
// 数据属性描述符
Object.defineProperty(obj, 'address', {
value: '广州市', // 默认值 undefined
// 不能修改、删除、重新定义
configurable: false, // 默认值 false
// 不能被枚举
enumerable: false, // 默认值 false
// 不能修改属性值
writable: false // 默认值 false
})
// 测试 configurable 的作用
// delete obj.name
// console.log(obj.name)
// delete obj.address
// console.log(obj.address)
// // Uncaught TypeError: Cannot redefine property: address
// Object.defineProperty(obj, 'address', {
// value: '北京市',
// configurable: true
// })
// 测试 enumerable 的作用
// for (var key in obj) {
// console.log(key)
// }
// console.log(Object.keys(obj))
// 测试 writable 的作用
// obj.address = '北京市'
// console.log(obj.address)
存取属性描述符
存取属性描述符有如下四个特性:
[[configurable]]:表示属性是否可以通过 delete 删除属性,是否可以修改它的的特性,或者是否可以将它修改为存取属性描述符- 直接在一个对象上定义某个属性时,这个属性的 configurable 值为 true
- 通过属性描述符定义一个属性时, configurable 值为 false
[[enumerable]]:表示属性是否可以通过for-in或者Object.keys()返回该属性- 直接在一个对象上定义某个属性时,这个属性的 enumerable 值为 true
- 通过属性描述符定义一个属性时, enumerable 值为 false
[[get]]:获取属性时会执行的函数。默认值为 undefined[[set]]:设置属性时会执行的函数。默认值为 undefined
js
var obj = {
name: 'why',
age: 18,
_address: '广州市'
}
// 存取属性描述符
// 1、隐藏某一个私有属性,不希望直接被外界使用和赋值
// 2、如果我们希望接拦截获取某一属性,访问和设置值的过程时,也会使用存取属性描述符
Object.defineProperty(obj, 'address', {
// 不能修改、删除、重新定义
configurable: false, // 默认值 false
// 不能被枚举
enumerable: false, // 默认值 false
get: function () {
foo()
return this._address
},
set: function (newValue) {
bar()
this._address = newValue
}
})
function foo() {
console.log('获取了一次address的值')
}
function bar() {
console.log('设置了一次address的值')
}
obj.address = '北京市'
console.log(obj.address)