资讯专栏INFORMATION COLUMN

面对对象严格模式

paraller / 3251人阅读

摘要:对只读属性赋值会报错删除不可配置的属性会报错只设置了取值器的属性不可写严格模式下,对一个只有取值器没有存值器的属性赋值,会报错。严格模式禁止这种用法,全局变量必须显式声明。严格模式明确规定,函数内部使用将会报错。

设计目的
启用方法
显式报错
只读属性不可写
只设置了取值器的属性不可写
禁止扩展的对象不可扩展
eval、arguments 不可用作标识名
函数不能有重名的参数
禁止八进制的前缀0表示法
增强的安全措施
全局变量显式声明
禁止 this 关键字指向全局对象
禁止使用 fn.callee、fn.caller
禁止使用 arguments.callee、arguments.caller
禁止删除变量
静态绑定
禁止使用 with 语句
创设 eval 作用域
arguments 不再追踪参数的变化
向下一个版本的 JavaScript 过渡
非函数代码块不得声明函数
保留字
参考链接

1.设计目的
明确禁止一些不合理、不严谨的语法,减少 JavaScript 语言的一些怪异行为。
增加更多报错的场合,消除代码运行的一些不安全之处,保证代码运行的安全。
提高编译器效率,增加运行速度。
为未来新版本的 JavaScript 语法做好铺垫。

2.启用办法
"use strict";
2.1(1) 整个脚本文件


2.2(2)单个函数

use strict放在函数体的第一行,则整个函数以严格模式运行。

function strict() {
"use strict";
return "这是严格模式";
}

function strict2() {
"use strict";
function f() {

return "这也是严格模式";

}
return f();
}

function notStrict() {
return "这是正常模式";
}

2.3合并脚本
可以考虑把整个脚本文件放在一个立即执行的匿名函数之中。

(function () {
"use strict";
// some code here
})();

3.显式报错
3.1只读属性不可写
严格模式下,设置字符串的length属性,会报错。

"use strict";
"abc".length = 5;
// TypeError: Cannot assign to read only property "length" of string "abc"
上面代码报错,因为length是只读属性,严格模式下不可写。正常模式下,改变length属性是无效的,但不会报错。

严格模式下,对只读属性赋值,或者删除不可配置(non-configurable)属性都会报错。

// 对只读属性赋值会报错
"use strict";
Object.defineProperty({}, "a", {
value: 37,
writable: false
});
obj.a = 123;
// TypeError: Cannot assign to read only property "a" of object #

// 删除不可配置的属性会报错
"use strict";
var obj = Object.defineProperty({}, "p", {
value: 1,
configurable: false
});
delete obj.p
// TypeError: Cannot delete property "p" of #

3.2只设置了取值器的属性不可写

严格模式下,对一个只有取值器(getter)、没有存值器(setter)的属性赋值,会报错。

"use strict";
var obj = {
get v() { return 1; }
};
obj.v = 2;
// Uncaught TypeError: Cannot set property v of # which has only a getter
上面代码中,obj.v只有取值器,没有存值器,对它进行赋值就会报错。
3.3禁止扩展的对象不可扩展

严格模式下,对禁止扩展的对象添加新属性,会报错。

"use strict";
var obj = {};
Object.preventExtensions(obj);
obj.v = 1;
// Uncaught TypeError: Cannot add property v, object is not extensible
上面代码中,obj对象禁止扩展,添加属性就会报错

3.4eval、arguments 不可用作标识名

严格模式下,使用eval或者arguments作为标识名,将会报错。下面的语句都会报错。

"use strict";
var eval = 17;
var arguments = 17;
var obj = { set p(arguments) { } };
try { } catch (arguments) { }
function x(eval) { }
function arguments() { }
var y = function eval() { };
var f = new Function("arguments", ""use strict"; return 17;");
// SyntaxError: Unexpected eval or arguments in strict mode

3.5函数不能有重名的参数

正常模式下,如果函数有多个重名的参数,可以用arguments[i]读取。严格模式下,这属于语法错误。

function f(a, a, b) {
"use strict";
return a + b;
}
// Uncaught SyntaxError: Duplicate parameter name not allowed in this context

4.增强的安全措施
4.1全局变量显式声明

正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明。

"use strict";

v = 1; // 报错,v未声明

for (i = 0; i < 2; i++) { // 报错,i 未声明
// ...
}

function f() {
x = 123;
}
f() // 报错,未声明就创建一个全局变量
因此,严格模式下,变量都必须先声明,然后再使用。

4.2禁止 this 关键字指向全局对象

正常模式下,函数内部的this可能会指向全局对象,严格模式禁止这种用法,避免无意间创造全局变量。

// 正常模式
function f() {
console.log(this === window);
}
f() // true

// 严格模式
function f() {
"use strict";
console.log(this === undefined);
}
f() // true
上面代码中,严格模式的函数体内部this是undefined。

这种限制对于构造函数尤其有用。使用构造函数时,有时忘了加new,这时this不再指向全局对象,而是报错。

function f() {
"use strict";
this.a = 1;
};

f();// 报错,this 未定义
严格模式下,函数直接调用时(不使用new调用),函数内部的this表示undefined(未定义),因此可以用call、apply和bind方法,将任意值绑定在this上面。正常模式下,this指向全局对象,如果绑定的值是非对象,将被自动转为对象再绑定上去,而null和undefined这两个无法转成对象的值,将被忽略。

// 正常模式
function fun() {
return this;
}

fun() // window
fun.call(2) // Number {2}
fun.call(true) // Boolean {true}
fun.call(null) // window
fun.call(undefined) // window

// 严格模式
"use strict";
function fun() {
return this;
}

fun() //undefined
fun.call(2) // 2
fun.call(true) // true
fun.call(null) // null
fun.call(undefined) // undefined
上面代码中,可以把任意类型的值,绑定在this上面。

4.3禁止使用 fn.callee、fn.caller

函数内部不得使用fn.caller、fn.arguments,否则会报错。这意味着不能在函数内部得到调用栈了。

function f1() {
"use strict";
f1.caller; // 报错
f1.arguments; // 报错
}

f1();
4.4禁止使用 arguments.callee、arguments.caller

arguments.callee和arguments.caller是两个历史遗留的变量,从来没有标准化过,现在已经取消了。正常模式下调用它们没有什么作用,但是不会报错。严格模式明确规定,函数内部使用arguments.callee、arguments.caller将会报错。

"use strict";
var f = function () {
return arguments.callee;
};

f(); // 报错
4.5禁止删除变量

严格模式下无法删除变量,如果使用delete命令删除一个变量,会报错。只有对象的属性,且属性的描述对象的configurable属性设置为true,才能被delete命令删除。

"use strict";
var x;
delete x; // 语法错误

var obj = Object.create(null, {
x: {

value: 1,
configurable: true

}
});
delete obj.x; // 删除成功

5.静态绑定

JavaScript 语言的一个特点,就是允许“动态绑定”,即某些属性和方法到底属于哪一个对象,不是在编译时确定的,而是在运行时(runtime)确定的
严格模式对动态绑定做了一些限制。某些情况下,只允许静态绑定。也就是说,属性和方法到底归属哪个对象,必须在编译阶段就确定。这样做有利于编译效率的提高,也使得代码更容易阅读,更少出现意外。
5.1禁止使用 with 语句

严格模式下,使用with语句将报错。因为with语句无法在编译时就确定,某个属性到底归属哪个对象,从而影响了编译效果。

"use strict";
var v = 1;
var obj = {};

with (obj) {
v = 2;
}
// Uncaught SyntaxError: Strict mode code may not include a with statement
5.2创设 eval 作用域

正常模式下,JavaScript 语言有两种变量作用域(scope):全局作用域和函数作用域。严格模式创设了第三种作用域:eval作用域。
eval所生成的变量只能用于eval内部。

(function () {
"use strict";
var x = 2;
console.log(eval("var x = 5; x")) // 5
console.log(x) // 2
})()
上面代码中,由于eval语句内部是一个独立作用域,所以内部的变量x不会泄露到外部。

注意,如果希望eval语句也使用严格模式,有两种方式。

// 方式一
function f1(str){
"use strict";
return eval(str);
}
f1("undeclared_variable = 1"); // 报错

// 方式二
function f2(str){
return eval(str);
}
f2(""use strict";undeclared_variable

5.3arguments 不再追踪参数的变化
变量arguments代表函数的参数。严格模式下,函数内部改变参数与arguments的联系被切断了,两者不再存在联动关系。

function f(a) {
a = 2;
return [a, arguments[0]];
}
f(1); // 正常模式为[2, 2]

function f(a) {
"use strict";
a = 2;
return [a, arguments[0]];
}
f(1); // 严格模式为[2, 1]
上面代码中,改变函数的参数,不会反应到arguments对象上来

6.向下一个版本的 JavaScript 过渡
6.1非函数代码块不得声明函数

ES6 会引入块级作用域。为了与新版本接轨,ES5 的严格模式只允许在全局作用域或函数作用域声明函数。也就是说,不允许在非函数的代码块内声明函数。

"use strict";
if (true) {
function f1() { } // 语法错误
}

for (var i = 0; i < 5; i++) {
function f2() { } // 语法错误
}
上面代码在if代码块和for代码块中声明了函数,ES5 环境会报错。

注意,如果是 ES6 环境,上面的代码不会报错,因为 ES6 允许在代码块之中声明函数。

6.2保留字

为了向将来 JavaScript 的新版本过渡,严格模式新增了一些保留字(implements、interface、let、package、private、protected、public、static、yield等)。使用这些词作为变量名将会报错。

function package(protected) { // 语法错误
"use strict";
var implements; // 语法错误
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105782.html

相关文章

  • 面对对象编程

    摘要:也就是说,构造函数内部,指的是一个新生成的空对象,所有针对的操作,都会发生在这个空对象上。上面代码中,构造函数的语句,返回的是一个新对象。 一、创建对象 1.构造函数var Vehicle = function () { this.price = 1000;}; var v = new Vehicle();v.price // 1000 new命令本身就可以执行构造函数,所以后面的构...

    leeon 评论0 收藏0
  • 面对对象(五)this使用注意点

    摘要:绑定回调函数的对象前面的按钮点击事件的例子,可以改写如下。方法有一些使用注意点。上面代码中,方法会调用回调函数。但是,方法的回调函数内部的却是指向全局对象,导致没有办法取到值。 4.1避免多层 this由于this的指向是不确定的,所以切勿在函数中包含多层的this。 var o = { f1: function () { console.log(this); var f2 = fu...

    hearaway 评论0 收藏0
  • 2017双11交易系统TMF2.0技术揭秘,实现全链路管理

    摘要:阿里巴巴资深技术专家毗卢毗卢,阿里巴巴资深技术专家,主导设计了框架,并基于该框架完成交易平台架构升级改造,目前负责商品中心,专注电商领域业务建模与工程交付相结合的研究与平台推广。 摘要: 本文是《2017双11交易系统TMF2.0技术揭秘》演讲整理,主要讲解了基于TMF2.0框架改造的交易平台,通过业务管理域与运行域分离、业务与业务的隔离架构,大幅度提高了业务在可扩展性、研发效率以及可...

    myeveryheart 评论0 收藏0
  • JavaScript中this总结

    摘要:普通函数调用函数在全局作用域下运行在非严格模式下,指向全局对象,在严格模式下,会变成。使用来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。即构造函数的指向它实例化出来的对象。 JavaScript中的this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。 先来列举一下都有哪些函数调用方式: 普通函数调用 对象方法调用 call()、apply()...

    ghnor 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<