JS 高级

233 阅读16分钟

JS 高级

  1. 对象

  2. 创建对象的三种方式

    字面量的方式

    var per1 = { name: '小米', age: 20, eat: function () { console.log('吃') } }

    调用系统的构造函数

    var per2 = new Object() per2.name = '小米' per2.age = 20 per2.eat = function () { console.log('吃') }

    自定义构造函数方式

    function Person(name, age) { this.name = name this.age = age this.eat = function () { console.log('吃') } } var per3 = new Person('小米', 20)

  3. 继承

  4. 继承的多种方式和优缺点

  5. 原型链继承

    function Parent() { this.name = 'ben' }

    function Child() {}

    Child.prototype = new Parent()

    const child = new Child()

    child.name // ben

    缺点:引用类型的属性值始终都会共享相应的值

    function Parent() { this.name = ['lucy', 'mack'] }

    function Child() {}

    Child.prototype = new Parent()

    const child1 = new Child()

    child1.name.push('william') child1.name // ['lucy', 'mack', 'william']

    const child2 = new Child() child2.name // ['lucy', 'mack', 'william']

  6. 借用构造函数(经典继承)

    function Parent() { this.name = 'ben' }

    function Child() { Parent.call(this) }

    const child = new Child() child.name // ben

    优点:1. 避免了引用类型的属性被所有实例共享

    function Parent() { this.name = ['lucy', 'mack'] }

    function Child() { Parent.call(this) }

    const child1 = new Child() child1.name.push('william') child1.name // ["lucy", "mack", "william"]

    const child2 = new Child() child.name // ['lucy', 'mack']

    优点:2. 可以在 Child 中向 Parent 传参

    function Parent (name) { this.name = name; }

    function Child (name) { Parent.call(this, name) }

    const child1 = new Child('lucy')

    child1.name // lucy

    const child2 = new Child('jack')

    child2.name // jack

  7. 组合继承

    function Parent() { this.name = 'ben', this.colors = ['red', 'blue', 'green'] } Parent.prototype.getName = function() { console.log(this.name) }

    function Child(name, age) { Parent.call(this, name) this.age = age }

    Child.prototype = new Parent() Child.prototype.constructor = Child // 或另一种写法 Child.prototype = Object.create(Parent.prototype, {constructor: Child})

    const child1 = new Child()

    child1.colors.push('black') child1.name // ben child1.getName() // ben child1.colors // ["red", "blue", "green", "black"]

    const child2 = new Child() child2.colors // ["red", "blue", "green"]

    缺点:调用两次父构造函数

    function Parent (name) { this.name = name this.colors = ['red', 'blue', 'green'] }

    Parent.prototype.getName = function () { console.log(this.name) }

    function Child (name, age) { Parent.call(this, name) this.age = age }

    Child.prototype = new Parent()

    var child1 = new Child('ben', '18')

    child1 // 发现 Child.prototype 和 child1 都有一个属性为colors,属性值为['red', 'blue', 'green']

  8. 原型式继承

就是es5 Object.create 的模拟实现,将传入的对象作为创建的对象的原型

var person = {
    name: 'ben',
    friends: ['lucy', 'mack']
}

function createObj(o) {
    function F() {}
    F.prototype = o
    return new F()
}

const person1 = createObj(person)
person1.name // ben

# 缺点:包含引用类型的属性值始终都会共享相应的值,跟原型链继承一样
const person2 = createObj(person)
person1.name = 'jack'
person2.name // ben
person1.friends.push('william')
person2.friends // ['lucy', 'mack', 'william']
  1. 寄生式继承

    function createObj(o) { const clone = Object.create(o) clone.sayName = function(){ console.log('hi') } return clone }

    缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法

  2. 寄生组合式继承

    function Parent(name) { this.name = name this.colors = ['red', 'blue', 'green'] }

    function Child(name,age) { Parent.call(this, name) }

    function F () {} F.prototype = new Parent()

    Child.prototype = new F()

    const child = new Child('ben') const child1 = new Child('jack') child.colors.push('black') child.colors // ['red', 'blue', 'green']

ES6 继承写法 class

 class Person {
    constructor(name) {
        this.name = name
    }

    getName() {
        console.log(this.name)
    }
}

class Child extends Person {
    constructor(name, age) {
        super(name)
        this.age = age
    }

    sayHi() {
        console.log('hi')
    }
}

const child = new Child('ben')
child.getName() // ben
  1. 闭包

  2. 定义

函数A 内部有一个函数B,函数B 可以访问到函数A 中的变量,那么函数B 就是闭包

function A() {
    let a = 1
    window.B = function() {
        console.log(a)
    }
}
# 这个例子可以反驳观点:闭包就是函数嵌套函数,然后返回一个函数,这个不完整的解释
  1. 作用

间接访问函数内部的变量

  1. 常见问题:解决var 定义函数的问题

    for(var i =1; i<= 5; i++){ setTimeout(function timer() { console.log(i) }, i*1000) }

    因为setTimeout 是个异步函数,所以会先把循环全部执行完毕,这时候i 就是6 了,所以会输出一堆6

三种解决方法:

  • 使用闭包

    for(var i =1; i<= 5; i++){ !function (j) { setTimeout(function timer() { console.log(j) }, j*1000) }(i) }

    首先使用了立即执行函数将i 传入函数内部,这个时候值就被固定在了参数j 上面不会改变,当下次执行timer 这个闭包的时候,就可以使用外部函数的变量j,从而达到目的

  • 使用setTimeout 的第三个参数,这个参数会被当成timer 函数的参数传入

    for(var i =1; i<= 5; i++){ setTimeout(function timer(j) { console.log(j) }, i*1000, i) }

  • 使用let 定义 i

    for (let i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i) }, i * 1000) }

  1. 深浅拷贝

  2. 浅拷贝

对象类型在赋值的过程中,其实是复制了地址,从而导致改变了一方其他也都被改变的情况

let a = {
    age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2

浅拷贝解决:

  • Object.assign() Object.assign() 只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址

    let a = { age: 1 } let b = Object.assign({},a) a.age = 2 console.log(b.age) // 1

  • 扩展运算符...

    let a = { age: 1 } let b = {...a} a.age = 2 console.log(b.age) // 1

  1. 深拷贝

浅拷贝只解决了第一层的问题,如果属性的值是对象的话,需要使用深拷贝

let a = {
  age: 1,
  jobs: {
    first: 'FE'
  }
}
let b = { ...a }
a.jobs.first = 'native'
console.log(b.jobs.first) // native

深拷贝解决:

  • JSON.parse(JSON.stringity(object)) 该方法的局限性:
    • 会忽略 undefined

    • 会忽略 symbol

    • 不会序列化函数

    • 不能解决循环引用的对象

      let a = { age: 1, jobs: { first: 'FE' } } let b = JSON.parse(JSON.stringify(a)) a.jobs.first = 'native' console.log(b.jobs.first) // FE

如果你有这么一个循环引用对象,你会发现并不能通过该方法实现深拷贝

let obj = {
  a: 1,
  b: {
    c: 2,
    d: 3,
  },
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)

在遇到函数、 undefined 或者 symbol 的时候,该对象也不能正常的序列化

let a = {
  age: undefined,
  sex: Symbol('male'),
  jobs: function() {},
  name: 'yck'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "yck"}
  • MessageChannel 可以拷贝undefined 和循环引用的对象,拷贝有函数的对象时,还是会报错

    MessageChannel创建了一个通信的管道,这个管道有两个端口,每个端口都可以通过postMessage 发送数据,而一个端口只有绑定了onmessage 回调方法,就可以接收从另一个端口传过来的数据

    var channel = new MessageChannel() var port1 = channel.port1 var port2 = channel.port2 port1.onmessage = function(event) { console.log('port1 收到来自port2 的数据:' + event.data) }

    port2.onmessage = function(event) { console.log('port2 收到来自port1 的数据:' + event.data) }

    port1.postMessage('发送给port2') // port2 收到来自port1 的数据:发送给port2 port2.postMessage('发送给port1') // port1 收到来自port2 的数据:发送给port1

    let obj = { a: 1, b: { c: 2, d: 3, }, f: undefined } obj.c = obj.b; obj.e = obj.a obj.b.c = obj.c obj.b.d = obj.b obj.b.e = obj.b.c

    function deepCopy(obj) { return new Promise((resolve) => { const {port1, port2} = new MessageChannel() port2.onmessage = ev => resolve(ev.data) port1.postMessage(obj) }) }

    deepCopy(obj).then((copy) => { let copyObj = copy console.log(copyObj, obj) // {a: 1, b: {…}, f: undefined, c: {…}, e: 1} console.log(copyObj == obj) // false }) 或者 => const test = async () => { const clone = await deepCopy(obj) console.log(clone) } test()

  1. 原型

  2. 原型的作用

解决数据共享,节省内存空间

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.eat = function () {
    console.log('吃')
  }
}

var per01 = new Person('小白', 20);
var per02 = new Person('小黑', 20);
console.log(per01.eat == per02.eat) // false

低级解决方法

function myEat() {
  console.log('吃饭')
} 

# 存在弊端
var myEat = 10

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.eat = myEat;
}

var per01 = new Person('小白', 20);
var per02 = new Person('小黑', 20);

per01.eat = 10

console.log(per01.eat == per02.eat) // true

通过原型解决

function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.eat = function () {
  console.log('吃')
}

var per01 = new Person('小白', 20);
var per02 = new Person('小黑', 20);

console.log(per01.eat == per02.eat) // true
  1. 原型中的方法是可以相互访问的

    // 原型中的方法,是可以相互访问的 function Animal(name, age) { this.name = name; this.age = age; } // 原型中添加方法 Animal.prototype.eat = function () { console.log('吃') this.play() } Animal.prototype.play = function () { console.log('玩') this.sleep() } Animal.prototype.sleep = function () { console.log('睡') }

    // 实例化对象,并初始化 var dw = new Animal('小米', 20) dw.eat()

  2. 实例对象使用属性和方法层层的搜索

    实例对象使用的属性或者方法,先在实例中查找,找到了则直接使用,找不到则,去实例对象所指向构造函数的原型对象中找,找到了则使用,找不到则报错

    function Person(age, sex) { this.age = age; this.sex = sex; this.eat = function () { console.log('构造函数中的吃') } }

    Person.prototype.sex = "女"

    var per = new Person(20, '男') console.log(per.sex) // 男

  3. 函数

  4. 函数声明方式

  • 函数声明

    function f1() { console.log('我是函数') } f1()

  • 函数表达式声明(推荐)

    var f2 = function () { console.log('我也是一个函数') } f2()

  1. 创建对象的三种方式

    字面量的方式

    var per1 = { name: '小米', age: 20, eat: function () { console.log('吃') } }

    调用系统的构造函数

    var per2 = new Object() per2.name = '小米' per2.age = 20 per2.eat = function () { console.log('吃') }

    自定义构造函数方式

    function Person(name, age) { this.name = name this.age = age this.eat = function () { console.log('吃') } } var per3 = new Person('小米', 20)

  2. this指向

  3. 先看几个函数调用的场景

    函数自调用,this 一定是window

    function foo() { console.log(this) } foo() // window

    谁调用了函数,谁就是this

    const obj = { foo: foo } obj.foo() // {}

    new 方式,this 永远绑定在了实例上,不会被任何方式改变this

    const c = new foo() // foo {}

    箭头函数没有this,箭头函数中的this 只取决包裹箭头函数的第一个普通函数的this

    function a() { return () => { return ()=> { console.log(this) } } } a()()() // window

    bind 这些改变上下文,对于函数来说,this 取决于第一个参数,如果第一个参数为空,那么就是window

    let a = {} let fn = function() { console.log(this) } fn.bind(a)() // {} fn.bind().bind(a)() // window

  4. 实例(面试题)

    var length = 10 function fn() { console.log(this.length) }

    var obj = { length: 5, method: function(fn){ fn() arguments0 } }

    obj.method(fn) // 10 1 obj.method(fn, 123) // 10 2

    1. 存在疑虑的地方,obj.method(fn) 为什么不是获取obj对象的length属性值5?

    注意看清楚:fn() 是自调用函数,this指向window

    2. arguments0 表示什么意思?

    arguments是一个数组(对象),arguments[0] == fn,所以arguments0 等价于 arguments.fn(),this指向arguments

  5. apply、call、bind

  6. apply

  • 语法

函数名字.apply(对象,[参数1,参数2,...])

方法名字.apply(对象,[参数1,参数2,...])

  1. call
  • 语法

函数名字.call(对象,参数1,参数2,...)

方法名字.call(对象,参数1,参数2,...)

# 函数的调用,改变 this 的指向
function f1(x, y) {
  console.log((x+y)+":===>"this)
  return "这是函数的返回值"
}
// apply 和 call 调用
var r1 = f1.apply(null, [1, 2]) // 此时 f1 中的 this 是 window
console.log(r1)
var r2 = f1.call(null, 1, 2) // 此时 f1 中的 this 是 window
console.log(r2)

# 改变 this 的指向
console.log('==============')
var obj = {
  age: 10,
  name: '小马'
}
// 本来 f1 函数是 window 对象的,但是传入 obj 之后,f1 函数此时就是 obj 对象的
var r3 = f1.apply(obj, [1, 2]) // 此时 f1 中的 this 是 obj
console.log(r3)
var r4 = f1.call(obj, 1, 2) // 此时 f1 中的 this 是 obj
console.log(r4)

# 方法改变 this 的指向
console.log('==============')
function Person(age) {
  this.age = age 
}
Person.prototype.sayHi = function (x, y) {
  console.log((x+y)+':===>'+this) //是实例对象
}

function Student() {}
var per = new Person(10) // 实例对象
var stu = new Student() // 实例对象
// sayHi 方法是 per 实例对象的
per.sayHi(1, 2)
per.sayHi.apply(stu, [1, 2])
per.sayHi.call(stu, 1, 2)
  1. bind
  • 语法

函数名字.bind(对象,参数1,参数2,...) ----> 返回值是复制之后的这个函数

方法名字.bind(对象,参数1,参数2,...) ----> 返回值是复制之后的这个方法

function Person(age) {
  this.age = age
} 
Person.prototype.play = function () {
  console.log(this +':===>'+ this.age)
}

function Student(age) {
  this.age = age
}

var per = new Person(10)
var stu = new Student(20)
// 复制了一份
var ff = per.play.bind(stu)
ff()
  1. 类型转换

  2. 数据类型

  3. 检测数据类型

  4. typeof()

  • 除了null 都可以显示正确的类型,所以typeof 是否能正确判断类型?不能

  • typeof 对于对象来说,除了函数都会显示object

    typeof NaN // number typeof undefined // undefined typeof '1' // string typeof true // boolean typeof Symbol() // symbol typeof null // object(这是错误的)

  1. 区分对象的三种方式
  • instanceof 内部机制是通过原型链来判断的

    [] instanceof Array // true

  • x.proto.constructor

    [].__proto__constructor // Array(){}

  • 指定重写后的toString 方法

    Object.prototype.toString.call(123) // object Number Object.prototype.toString.call({}) // object Object Object.prototype.toString.call([]) // object Array

  1. == VS ===

  2. ==

对于 == 来说,如果对比双方的类型不一样的话,就会进行类型转换。

var a = {}
a == true // false,因为a.toString -> '[object Object]' -> NaN
a == false // false
null == undefined // true
NaN == NaN // false
null == 0 // false
undefined == 0 // false
null == false // false
new Object() == new Object() // false
[1,2] == [1,2] // false
  1. null 和 undefined

undefined: 表示一个变量最原始的状态,而非人为操作的结果

null: 表示一个对象被人为的重置为空对象,而非一个变量最原始的状态

  1. ===

对于 === 来说,判断两者类型和值是否相同

NaN === NaN // false
  1. 转换规则

  2. 类型转换表格

原始值 转换目标 结果
转布尔值
number 布尔值 除了0、-0、NaN 都为 true
string 布尔值 除了空串都为 true
undefined、null 布尔值 false
引用类型 布尔值 true

            	转字符串	                                        

number 字符串 5 => '5' NaN => 'NaN'
数组 字符串 [] => ' ' [1,2] => '1,2' [{a: 1}] => '[object Object]' 对象 字符串 '[object Object]'

            	转数字 	                                        

string 数字 '1' => 1,'a' => NaN 除了包含数字字符的字符串转数字,其他情况NaN 数组 数字 空数组为0,存在一个元素且为数字转数字,其他情况NaN
null 数字 0
undefined 数字 NaN
除了数组的引用类型 数字 NaN
Symbol 数字 抛错
true 数字 1
false 数字 0

  1. 对象转原始类型
  • 对象在转换类型的时候,会调用内置的[[ToPrimative]] 函数
  • 该函数的算法逻辑:
    • 如果已经是原始类型了,那就不需要转换了

    • 调用x.valueOf() 转换为基本类型,返回转换的值

    • 调用x.() 转toString() 换为基本类型,返回转换的值

    • 如果都没有返回原始类型,就会报错

      重写Symbol.toPrimitive,该方法在转原始类型时调用优先级最高

      let a = { valueOf() { return 0 }, toString() { return '1' }, Symbol.toPrimitive { return 2 } } 1 + a // => 3

  1. 显示类型转换和隐式类型转换

  2. 显示类型转换的方法

  • Number()
  • Boolean()
  • String()
  • toString()
  1. 隐式类型转换

js 的隐式类型转换是相对于显示类型转换来说的

  1. 运算

  2. 递增递降操作符

特殊

  • 是对象时,先调用对象的valueOf() 方法,以取得一个可供操作的值,如果结果是NaN,调用toString() 方法

    var s1 = '1', s2 = '12abc', b = true, o = { valueOf: function(){ return -1 } }; ++s1 // 2 ++s2 // NaN ++b // 2 ++o // 0

  1. 一元加减操作符(减法同理)

特殊

  • 如是布尔值,true 转换为1,false 转换为0

  • 如是null,返回0

  • 如是undefined,返回NaN

  • 如是字符串,规则

    • 如字符串只包含数字(包括前面带正负号的情况),将其转换为十进制数值
    • 如字符串中包含有效的浮点格式,将其转换为对应的浮点数值
    • 如字符串中包含有效的十六进制格式,例如“oxf”,将其转换为相同大小的十进制整数值
    • 如字符串为空,转换为0
    • 如字符中包含除上述格式之外的字符,将其转换为NaN
  • 如果是对象,调用对象的valueOf() 方法,再依照前面的规则转换返回的值,如果转换结果为NaN,调用对象的toString() 方法,再依照前面的规则转换

    var s1="1", s2="12abc", s3="0xf",
    s4="",
    b=true, f="1.1", f1="1.1.2", n=null,
    u=undefined,
    o={valueOf:function(){ return -1; } };

    +s1 //1 +s2 //NaN +s3 //15 +s4 //0 +b //1 +f //1.1 +f1 //NaN +n //0 +u //NaN +o //-1

  1. 加法运算(减法同理)
  • 运算中其中一方为字符串,那么就会把另一方也转换为字符串
  • 如果一方不是字符串或者数字,那么会将它转换为数字或者字符串
  • 如有一个操作数是NaN,结果是NaN

加法和减法的区别:

  • 如果一个操作数是字符串、布尔值、null、undefined,会调用Number() 函数将其转换为数值,在计算(加法是拼接)

    1 + '1' // '11' 1 + '1' - '2' // 9 true + true // 2 100 + true // 101 100 + false // 100 100 + undefined // NaN '100' + undefined // '100undefined' 100 + null // 100 '100' + null // '100null' 4 + [1,2,3] // '41,2,3' 'a' + + 'b' // "aNaN",因为 + 'b' 等于 NaN

    '5' - '5' // 0 1 - undefined // NaN

  1. 除了加法运算
  • 只要其中一方是数字,那么另一方就会被转为数字

    4 * false // 0 4 * true // 4 4 * '' // 0 4 * 'a' // NaN 4 * [] // 0 4 * [1] // 4 4 * [1,2] // NaN 4 * undefined // NaN 4 * null // 0

  1. 关系操作符

    < <= >=

特殊:

  • 如两个操作数都是字符串,则比较两个字符串对应的字符编码值

  • 如是对象,则调用对象的valueOf() 方法,如没有则调用toString() 方法

    let a = { valueOf() { return 0 }, toString() { return '1' } }

    console.log(a > -1) // true,因为 a 是对象,所以会通过 valueOf 转换为原始类型再比较值

    'a' > 'B' // true B的字符编码是66,a的字符编码是97 3 > NaN // false 3 < NaN // false 'a' > NaN // false

  1. 语法

  2. if 语句

  3. while 语句

  4. for in 时的类型转换

  5. alert 时存在的隐式类型转换

    String.prototype.fn = function(){return this}; var a = 'hello'; alert(typeof a.fn()); //-->object alert(a.fn()); //-->hello

  6. 严格模式

  7. 介绍

  • 严格模式通过抛出错误来消除一些原有的静默错误
  • 严格模式修复了一些导致JavaScript 引擎难以执行优化的缺陷,如:相同的代码,严格模式可以比非严格模式下运行的更快
  • 严格模式禁用了在ECMAScript 的未来版本中可能会定义的一些语法
  1. 使用
  • 函数中使用

    function() { "use strict"; // 或者是 'use strict';

    }

  • 整个脚本中使用:在这个JavaScript 文件开头写'use strict';(或者是"use strict";)

  1. 怎么合理的使用严格模式

不推荐在整个脚本使用严格模式,这种语法存在缺陷。合并严格与非严格模式的脚本是有可能有问题,建议按照一个个函数去开启严格模式或者将整个脚本的内容用一个函数包括起来,然后在这个外部函数中使用严格模式,这样就可以消除合并的问题。

  1. 严格模式与非严格模式区别

  2. 严格模式下,不允许使用with

with的用法:with 就是为了封装某个对象,减少某个对象的调用

!function () {
    with({x:1}) {
        console.log(x) // 1
    }
}()

!function () {
    'use strict';
    with({x:1}) {
        console.log(x) // SyntaxError
    }
}()
  1. 严格模式下,不允许给未声明的变量赋值

  2. 严格模式下,arguments 变为参数的静态副本

非严格模式下,arguments 对象里的元素和对应的参数是指向同一个值的引用

!function (a) {
    arguments[0] = 100
    console.log(a) // 100
}(1)

!function (a) {
    'use strict';
    arguments[0] = 100
    console.log(a) // 1
}(1)

但是:传的参数是对象除外。arguments和形参共享传递

!function(a) {
	'use strict';
	console.log(a.x) //1
	arguments[0].x = 100
	console.log(a.x) //100
}({x: 1})
  1. 严格模式下,删除参数名,函数名报错

非严格模式返回false,静默失败(静默失败:不报错也没有任何效果)

!function(a) {
	console.log(a) //1
	console.log(delete a) //false
	console.log(a)  //1
}(1)

!function(a) {
    'use strict';
	console.log(a) //1
    delete a // SyntaxError
	console.log(a)  //1
}(1)
  1. 严格模式下,函数参数名重复报错

非严格模式最后一个重名参数会覆盖之前的重名参数

!function (a, a, b) {
    console.log(a + b)  // 5
}(1, 2, 3)

!function (a, a, b) {
    'use strict';
    console.log(a + b)  // SyntaxError
}(1, 2, 3)
  1. 严格模式下,删除不可配置(configurable = false)的属性报错

非严格模式返回false,静默失败

!function (a){
	var obj={};
    Object.defineProperty(obj,'a',{ configurable: false });
	console.log(delete obj.a); //flase
}(1);  
        
!function (a){
	'use strict';
	var obj={};
	Object.defineProperty(obj, 'a', { configurable: false });
	console.log(delete obj.a); //TypeError
}(1);
  1. 严格模式下,修改不可写(writable = false)的属性报错

    !function () { var obj = { a: 1 }; Object.defineProperty(obj, 'a', { writable: false }); obj.a = 2; console.log(obj.a); //1 //证明没有被修改 }();

    !function () { 'use strict'; var obj = { a: 1 }; Object.defineProperty(obj, 'a', {writable: false}); obj.a = 2; //TypeError }();

  2. 严格模式下,对象字面量重复属性名报错

    !function() { var obj = { x: 1, x: 2 }; console.log(obj.x); //2 }();

    !function() { 'use strict'; var obj = { x: 1, x: 2 };
    console.log(obj.x); //IE10+报错。IE7~9、Chrome、FF不报错,结果为:2 }();

  3. 严格模式下,禁止八进制字面量

    !function (){ console.log(0123); //83 }();

    !function (){ 'use strict'; console.log(0123); //SyntaxError }();

  4. 严格模式下,eval、arguments 成为关键字,不能用作变量,函数名

  5. 严格模式下,eval 变成了独立作用域

    !function() { eval('var evalVal = 2;'); console.log(typeof evalVal); //number }();

    !function() { 'use strict'; eval('var evalVal = 2;'); console.log(typeof evalVal); //undefined }();

  6. 严格模式下,给只读属性赋值报错

    !function () { 'use strict'; var obj = { get x() { return 17; } }; obj.x = 5; //TypeError }();

  7. 严格模式下,给不可扩展对象的新属性赋值报错

    !function () { 'use strict'; var fixed = { oldProp: "hello" }; Object.preventExtensions(fixed); fixed.newProp = "world"; //TypeError }()

  8. ES6中,严格模式下,禁止设置五种基本类型值的属性

    !function () { 'use strict'; undefined.aaa = 'aaa'; //TypeError null.bbb = 'bbb'; //TypeError false.ccc = 'ccc'; //TypeError (123).ddd = 'ddd'; //TypeError "hello".eee = 'eee'; //TypeError }();

  9. 严格模式下,一般函数调用(不是对象的方法调用,也不使用apply、call、bind等修改this),this 指向undefined,而不是全局对象

    !function () { function fun() { return this; } console.log( fun() ); //Window }();

    !function () { 'use strict'; function fun() { return this; } console.log( fun() ); //undefined }();

  10. 严格模式下,使用apply/call/bind,当传入参数是null/undefined时,this指向null/undefined,而不是全局对象

    !function () { function fun() { return this; } console.log( fun.apply(null) ); //Window console.log( fun.apply(undefined) ); //Window console.log( fun.call(null) ); //Window console.log( fun.call(undefined) ); //Window console.log( fun.bind(null)() ); //Window console.log( fun.bind(undefined)() ); //Window }();

    !function () { 'use strict'; function fun() { return this; } console.log( fun.apply(null) ); //null console.log( fun.apply(undefined) ); //undefined console.log( fun.call(null) ); //null console.log( fun.call(undefined) ); //undefined console.log( fun.bind(null)() ); //null console.log( fun.bind(undefined)() ); //undefined }();