Go to comments

JavaScript ES5.0标准模式


es发展史从es1.0一直到现在的es7.0,

目前通用的版本是es3.0、es5.0、es6.0(es6.0还是一个单独的课程),光说es3.0和es5.0因为这两个版本特别通用


一、ES3.0到ES5.0

es3.0到es5.0版本的升级革新意味着,摒弃一些老的语法,产生一些新的语法


为什么要摒弃一些老的语法?

因为现在的语法和原来的语法产生冲突了,产生冲突的部分要适当的摒弃,摒弃一些老语法,还诞生一些原来老语法里面没有的新语法,

比如es3.0和es5.0有的方法重了,但是一样的方法在es5.0里面得出的结果,和在es3.0里面得出的结果不一样。


但是es3.0到es5.0更新升级的时候,产生了一个非常有意思的问题,

现在浏览器是基于es3.0的方法,加上es5.0的新增方法使用的(基于es3.0 + es5.0新增的方法),也就是说我们现在调用的所有方法和语法,都是es3.0原有的和es3.0没有的es5.0有的。


现在有一个问题,

es3.0和es5.0产生冲突的那一部分怎么解决?

因为浏览器是基于es3.0和es5.0的新增方法,就是原来没有的现在有的,

产生冲突了,es3.0有的es5.0有的,用的是es3.0的。


下面是es3.0和es5.0产生冲突的部分,要用es5.0的方式来解决,怎么解决?

es3.0和es5.0产生冲突的部分,es5.0给这样的部分,定义了一个规则叫es5严格模式,不再兼容ES3.0的一些不规则语法,使用全新的ES5.0规范


二、ES5严格模式

现在执行有一种模式,一旦启动的es5.0的严格模式,

那么es3.0和es5.0产生冲突的部分就使用es5.0的,或者说用es5.0的方式,冲突是怎么样被处理的。


怎么来启动呢?

es5.0要启动必须遵循一个语法规则叫es5.0严格模式,

在整个页面的最顶端写上一行代码  "use strict";  这行代码是字符串形式展现的,写上以后整个程序就遵循es5.0的模式


看一个简单的例子,比如es5.0里面不允许使用 argument.callee,这是和es3.0冲突的

"use strict"; // 启动es5.0严格模式

function test(){
  console.log(arguments.callee);
}

test(); // Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

报错了,

TypeError 是类型错误,

'caller', 'callee', and 'arguments' properties may not be accessed on strict mode 这个错误是在es5.0严格模式下,不能拿es3.0的方法来执行


 "use strict"  写在整个页面的最顶端,就是为了给整个页面启动es5.0严格模式的,

启动完es5.0的严格模式,es3.0的部分方法就不能用了,其中 arguments.callee 就不能用了。


语法规定  "use strict"  要写在页面逻辑的最顶端。


什么叫逻辑的最顶端?

 "use strict"  前面可以有空格、回车,

但是不可有其它代码,要是有其他代码  "use strict"  这一句就不能被识别了。


这是启动es5.0的唯一模式,在第一行写字符串  "use strict" 

function test(){
  console.log(arguments.callee);
}

test();

"use strict"; // 写在这里不行


 "use strict"  写到页面的最顶端,启动的是全局的es5.0的严格模式,

如果把他写到局部也可以,可以写到局部的函数里面,但是也要写到最顶端的第一行


test区域和demo区域,分别遵循了不同的规则,下面的test函数遵循的是es5.0的规则,上面demo函数遵循的是es3.0的规则

function demo(){
  console.log(arguments.callee);
}

demo(); // 可以执行的


function test(){
  "use strict"; // 这个函数它遵循的的是es5.0的严格模式
  console.log(arguments.callee);
}

test(); // Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be...

执行test函数报错

image.png


es5.0的严格模式不再兼容es3.0的一些不规则语法,

和es3.0产生冲突的全面使用es5.0的新规范,这是es5.0严格模式的定义。


两种用法

第一写在全局的最顶端,

第二写在局部的最顶端,推荐使用局部的


 "use strict"  就是一行字符串,不会对不兼容严格模式的浏览器产生影响


问大家一个问题:

为什么"es5.0严格模式"启动方式,采用这种字符串 "use strict" 的启动形式,为什么不是一个函数呢?

考虑一个历史轴线的问题,es5.0严格模式一定是在浏览器的内核升级到了es5.0之后才好使,

如果这个浏览器比较老,没有升级到es5.0,在页面里面写 strict() 函数运行,没有这个函数肯定报错。


写字符串就没有这样的问题,比如写一个数字123,它就叫数字表达式它没什么意义,"use strict" 字符串同理

123;

"use strict";

假如浏览器识别不了这个字符串,至少不会报错,

这样写有一个向后兼容的作用,老版本浏览器保证不报错,新版本浏览器能识别,


所以采取用字符串的形式来表达"es5严格模式",

系统会识别第一行字符是不是  "use strict" ,如果是就会开启"es5严格模式。


三、ES5.0严格模式到底禁止了哪些东西呢?

1. with

2. arguments.callee、func.caller

3. 变量赋值前必须声明

4. 局部this必须被赋值 Person.call( null/undefined ) 赋值什么就是什么

5. 拒绝重复属性和参数


1、with

首先第一个es5.0严格模式不允许用 with

with() {

}


with特别强大,强大到什么程度呢?

var obj = {
  name : "obj",
}

var name = "window";

function test(){

  var age = 123;

  var name = 'scope';

  with(obj) {

    console.log(name); // 输出 obj

  }

}

test();

with里面的代码会按照正常的顺序执行,但是如果在 with( obj ) 括号里面填了一对象那它就了不得了,

它会把这个对象当做with圈定要执行的代码体的作用域链的最顶端,也就是说with会改变作用域链。

1. 现在在with里面去访问name,它最顶端看到的是对象obj,

2. 它会把obj当成最顶端的一个AO,链在它原来作用域链的最顶端

3. 所以这样访问的name是对象obj里面的打印出的"obj"


with的作用是可以改变作用域链,可以让它里面的代码的作用域链的顶端,变成 with(obj) 括号里面的对象,这个对象obj充当了with里面代码最直接的AO


with( obj )这样写,with里面访问变量name就直接到obj里面找name,把obj当成一个AO了(反正obj也是一个对象),把obj当成第一个域了,

如果obj里面没有,才会找test里面形成的AO里找,然后才会找GO。


如果访问age打印123,因为obj里面没有age

var obj = {
  name : "obj",
}

var name = "window";

function test(){

  var age = 123;

  var name = 'scope';

  with(obj) {

    console.log(name); // obj
    console.log(age);  // 123

  }

}

test();


如果在obj对象里面放一个变量age呢?打印的结果就是234

var obj = {
  name : "obj",
  age = 234; // obj里面放一个age
}

var name = "window";

function test(){

  var age = 123;

  var name = 'scope';

  with(obj) {

    console.log(name); // obj
    console.log(age); // 打印结果是234

  }

}

test();


with有什么作用呢?

with能简化代码,之前的命名空间说不太好比较麻烦,真正的命名空间的用法是这么来用的

var org = {
  department1 : {
    jc:{
      name: "abc",
      age: 123
    },
    deng:{
      name: "bce",
      age: 123
    }
  },
  department2: {
    zhangsan: {

    },
    lisi: {

    }
  }
}


with(org.department1.jc){
  console.log(name); // 这个方法体里面写的是jc的代码了,jc能访问到最直接的执行期上下文是"org.department1.jc"。
}

with(org.department1.deng){
  console.log(name);
}

with里面用name变量,直接用这个变量名就可以了,因为找的最直接的执行期上下文,就是with括号里的东西,这样能达到代码简化的一个问题。


document也是一个对象,上面会有很多对象和方法,在控制台document加"."上面有很多属性和方法

image.png


以后在用的时候,每次都document点什么什么东西,比如document.write

document.write('a');


最简化的写法是,不想每次都doucment点了,直接就把doucment放到with里面去,document.write 直接 write 就可以了

with(document){
  write('a');
}


为什么不用写点了?

document.{
  wirte: function(){

  }
}

// 1.因为直接write,要到write对应的执行期上下文里面找write函数
// 2.这个对应的执行性上下文是document
// 3.document里面有一个write,就像在AO里面拿这个函数一样,就把这write拿出来了,这是with的一个应用。

如果遇到一个非常长的东西,什么点什么什么点,还经常用的话,就直接给放到with里面,

但是不好的地方就是with过去强大了,改什么都情有可原,你改的是作用域链啊!


作用域链是经过很复杂的情况才生成的一个结构,作用域链改了之后,系统内核会消耗大量的效率,去更改这个作用域,会把程序会变的非常慢,

在原来的作用域链上,强加了一层作用域链,改变的结构改变的太深了。


如果作用域链是十层,这一修改就改了十层,新加的这一层相当于改了十一层(每一层都往得往下串),

这样达到一个效率的丧失,


所以es5.0的严格模式提高效率,with方法不可以用了

"use strict";

with(document){ // Uncaught SyntaxError: Strict mode code may not include a with statement

  write('a');

}

Strict mode code                                             严格模式下的代码,

may not include a with statement write('a')   不能包含一个write的模块


2、arguments.callee 和 func.caller

arguments.callee 和 func.caller 在es5.0严格模式下也不能用了

"use strict";

function test(){
  console.log(arguments.callee);
}

test(); // Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

es5严格模式下报错,'caller', 'callee', and 'arguments' 要连起来读的,不是 and arguments,arguments 可以用,

报错信息的意思是caller和callee以及其他的一些arguments的属性,不能被通过在"strict mode functions"严格模式的function里面不能应用了。


再看caller在es5严格模式下也不能用

"use strict";

function test(){
  console.log(test.caller);
}

function demo(){
  test();
}

demo(); // Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

这两个记住就行了,下面这个是重点


3、变量赋值前必须声明

es5.0严格模式规定,变量赋值前必须声明,

原来一个变量未经声明赋值,叫暗示全局变量归 window 所有,还有一个语法 imply global


es5.0严格模式规定,要么声明要么就别用

"use strict";

var a = b = 3; // Uncaught ReferenceError: b is not defined


在看一个

"use strict";

a = 3; // Uncaught ReferenceError: a is not defined


为什么要严格模式?

严格模式确实把代码的灵活度给缩减了,但是却让代码的犯错率更少了,

因为原来越松散犯错的概率越大,现在缩减了告诉我们只能这么用,就不容易报错了。


一般公司里面编程都推荐使用严格模式,因为严格模式下this什么的要求又变严了,把语法限制住了很难去报错,

原来es3里面这使也行,那么使也行并且还有不同的规则,

es5把一些不同的规则全禁止掉了,严格模式在开发过程中是有利于编程的。


4、局部this必须被赋值 Person.call( null/undefined ) 赋值什么就是什么

es5.0局部的this必须被赋值,而且赋什么就是什么

es3.0原来的语法里面,预编译的过程this指向window


es5.0严格模式规定,局部的 this 预编译的时候是空

"use strict";

function test(){
  console.log(this);
}

test(); // undefined


es5.0严格模式局部的 this 必须被赋值,而且赋什么就是什么。想要this有值,用 new 操作 test() 生成对象,这个this就有值了

"use strict";

function test(){
  console.log(this);
}

new test(); // test {}

为什么生成对象在控制台里面是这么显示?this返回一个对象,this是一个对象,代表这个构造函数test生成的对象

image.png


这个 test {} 为什么这样显示,为什么前面不写 object 后面写一个括号?

1. 前面的test是该对象的constructor的名,该对象的constructor的名是test,

2. 所以在控制台显示对象的时候,系统会把构造器的名写到前面,后面写上对象的标准形式括号


new操作 this 是个新对象,

不 new 操作 this 是undefined,

函数里this必须被赋值,或者  .call( {} )  里面写个对象进去,this指向就是空对象了

"use strict";

function test(){
  console.log(this);
}

test.call({}); // {}


在es5.0严格模式里,预编译this不再指向window,没有指向是空undefined,this必须被赋值,赋什么就是什么。


es5严格模式下,this变成数字123行吗?this就变成123,赋值什么就是什么

"use strict";

function test(){
  console.log(this);
}

test.call(123); // 123


但是这条语法在es3.0里面,call 也能赋值进去,也能改变this指向,但要改变一个原始值,系统会包装成包装类

function test(){
  console.log(this);
}

test.call(123); // Number {123}


Number {123}  这是包装类的显示形式,和包装类的显示形式一样

console.log(new Number(123)); // Number {123}


在es3.0里面改变this指向可以,但不能传原始值,传原始值系统给包装成对象,

但在es5.0严格模式里面传什么是什么。


es5.0严格模式下在全局的范围里this指向的是谁?es5.0严格模式下this指向还是window

"use strict";

console.log(this); // Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …


5、es5.0严格模式拒绝重复属性和参数是什么意思?

在es3.0里面重复的参数是不报错的,虽然没人这么写

function test(name, name){
  console.log(name);
}

test(1, 2); // 2


但是再es5.0里面重复的参数是报错的

"use strict";

function test(name, name){
  console.log(name);
}

test(1, 2); // Uncaught SyntaxError: Duplicate parameter name not allowed in this context


重复的参数在es5.0里面是不行的,然后重复的属性名在es5.0里面也是不行的,但有一个小区别是不报错

"use strict";

var obj = {
  name : "123",
  name : "234"
}

console.log(obj.name); // 234


官方规定,

es5.0是拒绝重复的属性和参数,这块属性重复的,但他不报错,

最后name代表一个值,打印结果是234,后面的会覆盖上面的。


6、es5里面能用eval吗?

eval()非常强大,eval()里面能把字符串当做代码来执行

var a = 123;

eval('console.log(a)'); // 123

这样一个  console.log(a)  字符串能在 eval() 里面执行,eval它会执行这个字符串,把字符串当代码来使


不幸的是尽管eval强大,但是在大家都认可的通用语法里规定,es3.0里面都不用使eval(),大家给一个规定eval是魔鬼。


因为eval能改变作用域,eval改变作用域的能力更强大

var global = 100;

function test() {

  global = 200;

  eval('console.log(global)');

}

test(); // 200

当不同种情况eval能变得作用域是不一样的,eval还有一个自己独立的作用域,

不说了,不说了,因为也确实没什么用,知道有eval这回事就行了



Leave a comment 0 Comments.

Leave a Reply

换一张