第一章 初识Javascript

一直以来都在使用JavaScript进行一些开发工作,但是仅仅局限于能够完成需求,对于这门语言一些深入和特别的地方并没有过多了解。所以从今天起就开始系统学习Javascript课程啦,感谢向军大叔的相关教学视频,讲解清晰通俗易懂,下面我将按照他的讲解顺序结合自己的思考进行记录总结,期待能够得到进一步的提高~

文章目录


1. Javascript发展历程

JavaScript发布至今已经有大约26年的时间了,在移动互联网日渐丰富的今天,作为直接面对用户的一门语言,其重要作用不言而喻。JavaScript目前能够应用到多种场景,包括浏览器网页端开发、服务器后台(Node.js)、移动端手机APP、跨平台的桌面应用程序,这门语言非常强大,应用范围广泛。那么我们就先从它大致的发展历程开始学习。


1.1 网景首发

1994年,网景公司发布了Navigator浏览器。在那个流量贵如油的年代里,如何节省流量的使用是那个年代众多人的追求。Navigator浏览器以其反应迅速、节省流量等特性,迅速占领了市场90%的占有率。

为了进一步节省流量,网景公司对表单进行了改造。之前网页端上并没有相关的脚本程序,因此用户提交的表单需要先发送到服务器上进行验证,如是否漏填某一项等,然后服务器再返回一定的结果,若用户填写不当,那么这就意味着造成了一定的流量浪费。倘若直接在网页端基于一定的规则对表单进行验证,那么将能够解决这一问题。因此,网景公司在1995年发布了JavaScript语言,用于在网页端进行一定的逻辑操作。1996年,JavaScript正式在Navigator浏览器中使用。

1.2 微软竞争

JavaScript这一利器丰富了网页端的内容,Navigator发展更加迅猛。作为巨头的微软哪能容忍自家浏览器的市场份额被如此抢占,因此在1996年发布了JScript语言,在自家的IE 3.0浏览器上使用。

1.3 标准统一

面对巨头微软的竞争,网景公司于1996年11月将JavaScript语言提交给了国际标准化组织(ECMA),希望成为国际标准。ECMA紧接着组织了第39号技术委员会进行标准的制定,成员包括微软、谷歌等巨头公司。终于,于1997年发布了ECMA-262标准,推出了浏览器标准语言ECMAScript 1.0。自此,纷争结束,浏览器标准语言正式步入发展轨道。我们现在常常说的ES5、ES6、ES7、ES8等其实就是ECMAScript标准的不同版本。
注意:ECMAScript指的是标准,而JavaScript是标准的实现形式。

2. 变量声明

JavaScript里面的变量都是弱类型的,即变量没有类型而值有类型,这意味着可以给同一个变量赋予不同类型的值。同时要求变量名应以字母、$或_开始,后面跟字母、数字或_。


2.1 声明

2.1.1 单个变量

常见的变量初始化写法为:

var test = 123;

但实际上这是下面这种写法的缩写形式:

var test;
test = 123;

也就是先声明,然后赋值。

2.1.2 多个变量

JavaScript支持我们一次性声明多个变量,写法如下:

var age = 123, name = '张三', sex = '男';

2.1.3 一个值赋给多个变量

写法如下:

var a = b = c = d = 'Javascript学习';

这样的话变量abcd均被赋予了值“JavaScript学习”

2.2 弱类型

2.2.1 举例

JavaScript的变量为弱类型,比如:

var a = 1;
a = '字符串类型';
a = null;
a = true;

可以看到变量a可以被分别赋值为Number类型、String类型、Object类型、Boolean类型等等。那么变量最新的类型变为它最后被赋予的那个值的类型,在这里为Boolean类型

2.2.2 typeof查看变量类型

如果我们希望查看当前变量的类型该怎么办?JavaScript给了一个typeof方法,可以用来检测变量类型,比如:

var a = 1;
console.log(typeof a); // number
a = '字符串类型';
console.log(typeof a); // string
a = null;
console.log(typeof a); // object
a = true;
console.log(typeof a); // boolean

3. 变量提升问题

任何语言一开始创建都不是十全十美的,JavaScrip中一直存在一个预编译上的问题,那就是变量提升。好在后来2015年后发布的ES6标准中采用增加letconst关键字的方式给与了解决。


3.1 普通流程

分析下面的代码:

console.log(a); //undefined
var a = 1;
console.log(a); // 1

可以发现,当在变量a“声明”之前就进行使用的话,会输出undefined,并没有报错;在“声明”之后使用,便会正常输出里面引用的值。那么,为何第一行会是这样的结果?原因在于当浏览器对我们的代码进行预编译时,首先会将使用var定义的变量中声明的部分提到代码的作用域的最前面,然后再进行解析编译工作,因此便出现了上面的情况。所以实际上解析后的代码其实是下面这样:

var a;
console.log(a); //undefined
a = 1;
console.log(a); // 1

这便是所谓的变量提升,因此在ES6发布之前一般都建议开发者在作用域的最前面先进行变量声明,然后再进行使用,但这种方式既不友好又不利于开发。

3.2 嵌套流程

除了在上述普通的顺序流程外,在嵌套流程中也存在这个问题,比如:

function hello () {
	if (false) {
		var a = 'hello world';
	}
	console.log(a);
}
hello(); // undefined

虽然在if语句中的代码不会执行,但是在里面给变量a进行了声明和赋值,因此在预编译的时候会把声明部分提升到作用域前面,也就是下面这样:

function hello () {
	var a;
	if (false) {
		a = 'hello world';
	}
	console.log(a); 
}
hello(); // undefined

3.3 函数声明提升

除了变量声明提升以外,函数声明也存在提升的现象。我们将上面的例子改造一下:

hello(); // undefined
function hello () {
	if (false) {
		var a = 'hello world';
	}
	console.log(a); 
}

这是因为在预编译时不仅提升了变量声明,也对函数声明也进行了提升,也就是下面的样子:

function hello () {
	var a;
	if (false) {
		a = 'hello world';
	}
	console.log(a); 
}
hello(); // undefined

==》那么,当函数是以表达式的方式书写的会是什么效果?比如下面这样:

world(); // Uncaught TypeError: world is not a function
var world = function () {
	if (false) {
		var a = 'hello world';
	}
	console.log(a); 
}

会报错,提示“world不是一个函数”,这说明了当以表达式的方式定义函数时,则不会将整个函数进行提升,只是把变量声明提升了,其他的还留在原地(可以理解为变量声明提升的现象):

var world;
world(); // Uncaught TypeError: world is not a function
world = function () {
	var a;
	if (false) {
		a = 'hello world';
	}
	console.log(a); 
}

3.4 函数声明提升优先于变量声明

当作用域中使用同一个名称进行变量声明和函数声明时,会将函数声明进行提升,变量声明虽然也进行提升,但会排在函数的后面,因此即使也进行了声明,但不起任何作用,造成变量没有提升的假象,比如:

hello(); // undefined
console.log(hello);
 /* ƒ hello() {
	if (false) {
		var a = 'hello world';
	}
	console.log(a); 
}*/

var hello = 123;
function hello () {
	if (false) {
		var a = 'hello world';
	}
	console.log(a); 
}

console.log(hello); // 123
hello();// Uncaught TypeError: hello is not a function

分析一下,在本段代码中使用名称hello声明了一个变量,也同时声明了一个函数,因此会将函数整体优先于变量进行提升,变量随后,当解析完变量声明后,函数被覆盖,即下面这样:

function hello () {
	var a;
	if (false) {
		a = 'hello world';
	}
	console.log(a); 
}
var hello; // (虽然也提升,但因为上面已经声明过,所以不起任何作用)

hello(); // undefined

console.log(hello);
 /* ƒ hello() {
	if (false) {
		var a = 'hello world';
	}
	console.log(a); 
}*/

hello = 123; // (覆盖)

console.log(hello); // 123
hello(); // Uncaught TypeError: hello is not a function

注意当函数以表达式的方式书写时,遵循变量声明的提升规则,比如:

hello(); // 函数声明

var hello = function () {
	console.log('表达式书写'); 
}

hello(); //表达式书写

function hello () {
	console.log('函数声明'); 
}

hello(); //表达式书写

这是因为预编译后变为了下面这样:

function hello () {
	console.log('函数声明'); 
}
var hello; // (虽然也提升,但因为上面已经声明过,所以不起任何作用)

hello(); //函数声明

hello = function () {
	console.log('表达式书写'); 
} // (覆盖)

hello(); //表达式书写

hello(); //表达式书写

3.5 小试牛刀

经过上面的学习,让我们用这道题练练手把~

function Foo() {
    getName = function () { alert (1); };
    return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
function getName() { alert (5);}
var getName = function () { alert (4);};

// 写出如下的输出结果:
Foo.getName();
getName();
Foo().getName();
new Foo.getName();
new Foo().getName();

提示:经过预编译后变成了下面这个样子:

function Foo() {
	var getName;
    getName = function () { alert (1); };
    return this;
}

function getName() { alert (5);}
var getName;

Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
getName = function () { alert (4);};

// 写出如下的输出结果:
Foo.getName();  // 2=>Foo的静态属性getName
getName(); // 4=>执行全局环境下的getName
Foo().getName(); // 1=>window.getName()
new Foo.getName(); // 2=>执行Foo的静态属性getName的构造函数
new Foo().getName(); // 3=>相当于Foo实例原型上的getName

4. 暂时性死区TDZ

ES6在推出letconst关键字的同时也对暂时性死区TDZ进行了定义,即:当程序的控制流程在新的作用域(module function 或 block作用域)进行实例化时,在此作用域中用let/const声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定,所以是不能被访问的,如果访问就会抛出错误。因此,在这运行流程进入作用域创建变量,到变量可以被访问之间的这一段时间,就称之为暂时死区。


4.1 全局作用域

我们来看看这个例子:

console.log(a); // undefined
var a = 'a';

console.log(b); // Uncaught ReferenceError: b is not defined
let b = 'b';

console.log(c); // Uncaught ReferenceError: c is not defined
const c = 'c';

这是因为变量ac是由letconst声明的,因此当在声明前使用时便会报错

4.2 函数作用域

当在函数中使用时,也有TDZ的存在,看这个例子:

// test(); // Uncaught ReferenceError: a is not defined
let a = 'a1';

function test () {
	console.log(a);
	let a = 'a2';
}

test(); // Uncaught ReferenceError: a is not defined

此时会报错,这是因为当在函数中有a的声明后,即使外部有声明,也不会使用外部的,只会考虑内部的 。如果我们改成下面的样子,则会正常运行:

// test(); // Uncaught ReferenceError: a is not defined
let a = 'a1';

function test () {
	console.log(a);
	// let a = 'a2';
}

test(); // a1

此时函数内部并没有声明变量a,因此会从外面去找,而当运行时外面有值,则使用;当无值,则按报错处理。值得一提的是,在这个例子中,其实函数声明发生了提升,也就是预编译变为了这样:

function test () {
	console.log(a);
	// let a = 'a2';
}

test(); // 此时使用便会报Uncaught ReferenceError: a is not defined错误

let a = 'a1';

test(); // a1

4.3 参数作用域

这次我们再看看在函数的参数中TDZ的情况:

let a = 1;
let b = 2;

function test (a=b, b=3){
	console.log(a); 
	console.log(b);
}
test(); // Uncaught ReferenceError: b is not defined

这是因为在函数的参数中,首先运行a=b,然而此时变量b并未被声明,因此发生了错误;倘若我们改成下面这样,便不会报错;

let a = 1;
let b = 2;

function test (a=3, b=a){
	console.log(a); 
	console.log(b);
}
test(); // 3 3

这是因为首先a有声明和赋值,轮到b的时候也可正常声明和赋值,因此不会报错。
注意:在参数中虽然没有对变量进行声明,但实际上会默认为参数设置let变量,另外值得注意的是三个作用域的范围为全局作用域>参数作用域>函数体作用域。其他拓展案例可以参考这里:JavaScript中TDZ的理解

5. var、let和const的共同点

三者间的共同点是上一级作用域中定义的变量,可以在函数中使用。这里涉及到一个规则:在当前块级找不到该变量的声明时,会向上一块级寻找,上一级没有那就再上一级找,直至找到或全局作用域;当前块级有定义时,则使用自己的,并且自己的与上一级的之间没有任何联系


5.1 全局定义,函数使用

首先看这样一个例子:

var a = 'a1';

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

test(); // a1

当前变量a是在全局作用域中定义的,test()函数中并未对其进行声明,因此会从全局作用域中找这个变量,进而使用它。这是用var定义的,那么如果用let或者const定义的,输出效果一样,即函数test()中可以使用在全局作用域中定义的a

let a = 'a1';
// const a = 'a1'; // (或者用const定义)

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

test(); // a1

5.2 全局定义, 函数也定义

上面是在函数中使用全局定义的变量(函数中没有定义),那么如果在函数中也有定义,结果是怎样的?我们来看这个例子:

let a = 'a1';
// const a = 'a2' // (这样也可以)
// var a = 'a2' // (这样也可以)

function test () {
	let a = 'a2';
	// const a = 'a2' // (这样也可以)
	// var a = 'a2' // (这样也可以)
	console.log(a);
}

test(); // a1 => 函数test()中定义的a

console.log(a); // a2 => 全局定义的a

可以看到,当在函数体中有定义变量a后,此函数便不再向上查找变量,只使用自己的。需要指出的是:在函数体中定义的这个变量与全局定义的没有任何联系,因此无论他们分别使用var/let/const中任何一个定义的,都不会相互影响,各用各的。

5.3 整个套娃

上面举例讲了变量的使用规则,现在我们用一个再复杂一点的例子巩固一下:


const a = 'a1';

function test () {
	var a = 'a2';
	console.log(a); // a2 => 一级函数test()中定义的a
	
	function test () {
		let a = 'a3';
		console.log(a); // a3 => 二级函数test()中定义的a
		
		function test () {
			console.log(a); // a3 => 二级函数test()中定义的a
		}
		test();
	}
	
	test ();
}

test();

console.log(a); // a1 => 全局定义的a

首先调用了一级函数test(),进入后发现自身有定义变量var a = 'a2',因此使用自己的,输出了a2;而后在内部调用了内部定义的二级函数test(),进入后发现自身有定义变量let a = 'a3',因此也使用自己的,输出了a3;接着在内部调用了内部定义的三级函数test(),进入后发现自身并没有定义变量a,所以从上级查找,找到了,于是输出了a3
最后一级函数test()结束,最后一行输出了全局定义的a,输出a1

补充一下:从这个套娃中我们还可以看到,函数体中函数本身也遵循上下级互无关系的这种规则

6. 全局污染太可怕

不使用任何关键字进行变量声明时,容易造成全局污染


我们先看下这个例子:
function test () {
	a = 'a2';
	console.log(a);
}

test(); // a2
console.log(a); // a2

可以发现变量a并未在全局作用域中被声明,但是依然可以被访问到,这是因为函数体中的变量a没有使用任何修饰符便进行了声明,造成了全局污染。
这种情况有何危害呢?看下面:

/* 假设该函数定义在另外一个js文件中*/
function test () {
	a = 'a2';
	console.log(a);
}

/* 在本js文件中使用 */
let a = 'a1';

test(); // a2

console.log(a); // a2

假若test()函数不是自己写的,那么在大规模协作中,自己虽然正常定义了变量a,但是却发现结果不是自己预料到的结果,因此会导致奇奇怪怪的事情发生,影响开发进程。那么,如何解决呢?JavaScript提供了一个严格模式,倘若变量声明时没有使用修饰符,则会报错:

"use strict"; // 使用严格模式

/* 假设该函数定义在另外一个js文件中*/
function test () {
	a = 'a2';
	console.log(a);
}

/* 在本js文件中使用 */
// let a = 'a1'; // 不报错
a = 'a1'; // 报错:Uncaught ReferenceError: a is not defined

test(); // a2

console.log(a); // a2

但是还有一个问题,那就是严格模式下,函数test()中也并未使用修饰符,但没有报错,这是因为在当前作用域中对变量a进行了声明,因此在函数中允许这样做,其规则遵循我们上一节讨论的情况;如果没有关于a的声明,那么一旦调用test()将会报错,具体可看最后一节“use strict严格模式高质量代码守卫”。所以全局污染错综复杂,最好的方式就是无论全局还是函数体中都要使用修饰符声明变量。

7. 块作用域的优点

使用var声明的变量存在于最近的函数或全局作用域中时,没有块级作用域的机制,因此也容易造成全局污染(let和const因加入了块级作用域的概念,不存在这种情况)


7.1 解决循环中的全局污染

我们先来看一个循环:

var i = 99;

for (var i = 0; i < 5; i++) {
	console.log(i); // 0 1 2 3 4
}

console.log(i); // 5

虽然我们在全局作用域中定义了变量i,但因为在for循环中也定义了变量i,由于var并没有块作用域这一概念,因此循环以后导致全局作用域中的i受到了污染。如果此时我们使用let来定义,那么就不会出现污染这一问题:

var i = 99;

for (let i = 0; i < 5; i++) {
	console.log(i); // 0 1 2 3 4
}

console.log(i); // 99

这是因为let声明的变量有块作用域这一概念,因此将该变量局限在了块中({...}),避免了全局污染的问题。

7.2 简化变量封装办法

在ES6提出块作用域之前,如果想要避免每个人写的程序片段的变量相互影响,只能利用函数作用域进行规制(因为var有函数作用域这一概念,可以思考上面第5节的内容);当有了块作用域概念后,我们在封装自己的变量名称时便更加简单了

7.2.1 传统的办法

传统办法是使用立即执行函数进行封装:

/* 将变量封装在立即执行函数中
   用来控制里面变量的作用域
   不至于全局污染 */
(function () {

		var $ = window.$ = {};
		$.a = 'a1';
		
		var b = 'b1';
		$.getB = function () {
			return b;
		}
		
}.bind(window)());

/* 调用 */
console.log($.a); // a1
console.log($.getB()); // b1
console.log($.b) // undefined
console.log(b); // Uncaught ReferenceError: b is not defined

可以发现通过这样使用变量b,没有产生全局污染的问题,但是书写起来比较繁杂,不太优雅。

7.2.2 引入块作用域概念后的办法

在ES6之后,我们可以利用块作用域的概念将上面的封装方法优化:

/* 将变量封装在块中
   不至于全局污染 */
{
	let $ = window.$ = {};
	$.a = 'a1';		
	
	let b = 'b1';
	$.getB = function () {
		return b;
	}
	
	var c = 'c1';
}

/* 调用 */
console.log($.getB()); // b1
console.log($.a); // a1
console.log(c); // c1
console.log(b); // Uncaught ReferenceError: b is not defined

这次我们便不需要函数辅助封装了,只需一对花括号({..})即可。但要记住这只对let声明的变量起作用,对于var是不起作用的。

var i = 99;

for (let i = 0; i < 5; i++) {
	console.log(i); // 0 1 2 3 4
}

console.log(i); // 99

8. 深入了解const常量声明

const与let基本相同,也有块作用域这个概念,唯一不同的是它用来定义常量,一旦定义好后在同一作用域中其代表的值便不能再次改变,并且我们一般约定其名称使用大写


const不允许更改值这一特性的原理其实是规定了该变量指向的内存空间不得改变,因此如果我们给const定义的常量为引用类型的,那么其内部的值仍然可以改变;若为基础类型的,那么其值便无法改变,如下:

const URL = 'https://123.4556.cn';

const NEWURL = {
	name: 'url',
};

const NEWNEWURL = [];


NEWURL.name = 'newname';
console.log(NEWURL.name); // newname

NEWNEWURL[0] = 123;
console.log(NEWNEWURL[0]); // 123

URL = '666'; // Uncaught TypeError: Assignment to constant variable.

再看看这种情况:

const URL = 'https://123.4556.cn';

{
	const URL = 'newurl';
	console.log(URL); //newurl
}

console.log(URL); //https://123.4556.c

这是因为块作用域的存在,所以两个URL之间并无联系,是相互独立的,所以也不会报错。这种情况在函数中也同样适用,这里便不再举例了。

9. window全局对象污染与重复声明

在全局作用域中使用var声明的变量将会作为window的一个属性,倘若该变量名与window的某个属性名一样,将会污染这个属性;var还允许开发者重复声明某个变量,这也是不好的


9.1 window全局变量污染

看这个例子:

var a = 'a1';
console.log(window.a); // a1

var screenLeft = 996;
console.log(window.screenLeft); // 996

let b = 'b1';
console.log(window.b); // undefined

变量a因为是使用var在全局作用域中声明的,因此被作为了window的一个属性来使用了。screenLeft作为window的一个固有属性,也被声明了,因此产生了污染现象;但是b由于是使用let声明的,不会将其绑定到window上,也就不存在了这个问题

9.2 重复声明

看这个例子:

var a = 'a1';
var a = 'a2';
console.log(a); // a2

let b = 'b1';
let b = 'b2'; // Uncaught SyntaxError: Identifier 'b' has already been declared

因为var声明的变量允许在同一个作用域内重复声明,因此并未发生错误;但let则不允许这样做。总结一下还是let比较好用

10. Object.freeze冻结变量

如果冻结变量后,变量也不可以修改了,使用严格模式会报出错误


还记得上面我们讲了const声明的变量,若为引用类型的,仍然可以修改里面的值,有哪种方法可以让其不得更改呢?可以使用Object.freeze()来冻结想要保持不动的变量,比如:

const a = {
	name: '张三',
	age: 19
}

// 变量冻结
Object.freeze(a); 

// 冻结后此句话便不起作用了
a.name = '李四';

console.log(a.name); // 张三

可以看到此时a.name不会发生变化。当然,除了const定义的外,let/var也是一样的效果;另外,我们还可以使用严格模式来让其报错,提高我们的代码水平:

// 严格模式
'use strict';

var a = {
	name: '张三',
	age: 19
}

// 变量冻结
Object.freeze(a); 

 // 报错: Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
a.name = '李四';

console.log(a.name); 

11. 变量与引用类型的传值与传址特性

基本类型的复制是值的复制,即传值;引用类型的复制是指针的复制,即传址


看这个传值的例子:

let a = 'a1'
let b = a;

a = 'a2'

console.log(a, b); // a2 a1

很明显,a变了以后,b没有变,这是因为他们之间的复制是值的复制,ab指向的是不同的内存空间。但是当值占用的内存空间很大的时候,便不再进行值的复制,而是只复制指向该块内存空间的指针,比如;

let a = {
	name: '张三'
}
let b = a;

b.name = '李四'

console.log(a, b); // {name: "李四"} {name: "李四"}

虽然只是改了b里面的值,但a的也变了,这就是因为他们指向的是同一块内存空间,所以会同时变化

12. null与undegined详解

JavaScript中,null是一个只有一个值的特殊类型,表示一个空对象引用,即将来可能指向一个对象;undefined 是一个没有设置值的变量,即表示变量声明过但并未赋过值


要想了解二者的关系,必须要从历史讲起:话说当年JavaScript作者在发布这门语言的时候,考虑到变量涉及到是传值和传址的问题,因此怎么在声明变量的时候就进行区分呢?所以就确定了下面的初始化写法:

let a = undefined; // 希望a用来保存值
let b = null; // 希望b用来保存引用对象的一个指针

深究二者的关系,可以说他们值相同,但是类型却不相同:

console.log(typeof null); // object

console.log(typeof undefined); // undefined

console.log(null == undefined); // true

console.log(null === undefined); // false

并且,对于未声明或声明了但未赋值的变量、无返回值的函数返回结果,都是undefined:

let a;
console.log(a); // undefined

// console.log(b); // Uncaught ReferenceError: b is not defined

let test1 = function () {}

function test2 () {}

console.log(test1()); // undefined => 空返回值,因此返回undefined

console.log(test2()); // undefined => 空返回值,因此返回undefined

13. use strict严格模式高质量代码守卫

严格模式在ES5中新增,是一个字面量表达式。该模式可以让我们及早发现错误,使代码更安全规范。主流框架都采用严格模式,严格模式也是未来JS标准,所以建议代码使用严格模式开发


严格模式的声明方式为在脚本或者函数的头部添加use strict表达式来声明,下面我们系统了解下严格模式究竟有哪些限制:

13.1 不允许使用未声明的变量

"use strict";
x = 3.14; // Uncaught ReferenceError: x is not defined
y = {p1:10, p2:20}; // Uncaught ReferenceError: y is not defined

13.2 不允许删除变量或对象、函数

"use strict";
var x = 3.14;
delete x; // Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.

function x(p1, p2) {};
delete x;  // Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.

13.3 不允许变量重名

"use strict";
function x(p1, p1) {}; // Uncaught SyntaxError: Duplicate parameter name not allowed in this context

13.4 不允许使用八进制

"use strict";
var x = 010; // Uncaught SyntaxError: Octal literals are not allowed in strict mode. 

13.5 不允许使用转义字符

"use strict";
var x = \010; // Uncaught SyntaxError: Invalid or unexpected token

13.6 不允许对只读属性赋值

"use strict";
var obj = {};
Object.defineProperty(obj, "x", {value:0, writable:false});

obj.x = 3.14;  // a.js:5 Uncaught TypeError: Cannot assign to read only property 'x' of object '#<Object>'

13.7 不允许对一个使用getter方法读取的属性进行赋值

"use strict";
var obj = {get x() {return 0} };

obj.x = 3.14; // Uncaught TypeError: Cannot set property x of #<Object> which has only a getter

13.8 不允许删除一个不允许删除的属性

"use strict";
delete Object.prototype; // Uncaught TypeError: Cannot delete property 'prototype' of function Object() { [native code] }

13.9 变量名不能使用 “eval” 字符串

"use strict";
var eval = 3.14; // Uncaught SyntaxError: Unexpected eval or arguments in strict mode

13.10 变量名不能使用 “arguments” 字符串

"use strict";
var arguments  = 3.14; // Uncaught SyntaxError: Unexpected eval or arguments in strict mode

13.11 不允许使用以下这种语句

"use strict";
with (Math){x = cos(2)}; // Uncaught SyntaxError: Strict mode code may not include a with statement

13.12 由于一些安全原因,在作用域 eval() 创建的变量不能被调用

"use strict";
eval ("var x = 2");
alert (x); // a.js:3 Uncaught ReferenceError: x is not defined

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

function f(){
    return !this;
} 
// 返回false,因为"this"指向全局对象,"!this"就是false

function f(){ 
    "use strict";
    return !this;
} 
// 返回true,因为严格模式下,this的值为undefined,所以"!this"为true。

因此,使用构造函数时,如果忘了加new,this不再指向全局对象,而是报错:

function f(){
    "use strict";
    this.a = 1;
};
f(); // Uncaught TypeError: Cannot set property 'a' of undefined

13.14 一些保留关键字禁止作为变量名使用

为了向将来Javascript的新版本过渡,严格模式新增了一些保留关键字:implements/interface/let/package/private/protected/public/static/yield,这些关键字若作为变量名使用将会报错,如:

"use strict";
var public = 1500; // Uncaught SyntaxError: Unexpected strict mode reserved word
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值