关于JavaScript
JavaScript的历史
背景
"1994年,网景公司(Netscape)发布了Navigator浏览器0.9版。这是历史上第一个比较成熟的网络浏览器,轰动一时。但是,这个版本的浏览器只能用来浏览,不具备与访问者互动的能力。......网景公司急需一种网页脚本语言,使得浏览器可以与网页互动
网景公司同当时刚刚发布Java的Sun公司携手,借Java的热度发布新的语言,这门语言"看上去与Java足够相似",但是比Java简单,使得非专业的网页作者也能很快上手”,且能在浏览器中直接运行,被命名为JavaScript。
1995年,Brendan Eich被指定为这种"简化版Java语言"的设计师。花费10天时间设计出了JavaScript的原型,“Javascript语言实际上是两种语言风格的混合产物----(简化的)函数式编程+(简化的)面向对象编程。”
(幸好他们没把scheme嵌进去)
不久之后,微软开始同网景竞争,开发出了自己的JScript。两套不同标准的语言给网页开发带来了无数困难。
1996年,网景正式向ECMA(欧洲计算机制造商协会)提交语言标准。
1997年,ECMA以JavaScript语言为基础制定了ECMAScript标准规范ECMA-262。JavaScript成为了ECMAScript最著名的实现之一。
参考:
- [1] wikipedia JavaScript
- [2] Javascript诞生记
可能存在的设计缺陷
-
(曾经)没有块级作用域 从C家族转换到js难免感到困惑,js没有块级作用域,在函数内部最接近的环境就是函数的局部环境。使用
var定义的变量即为全局变量。
ES6添加了let,const,多少解决了这个问题。 -
空对象 Java常常因为空对象而报错,但JavaScript拥有null和undefined。
null 是object的一种
undefined 则是一种表示未定义的数据类型
var foo;
alert(foo == null); // true
alert(foo == undefined); // true
alert(foo === null); // false
alert(foo === undefined); // true
===表示严格相等比较,除了内容还会比较两者的类型。foo不是null,只是未被定义。能用上null的地方实在没有多少。
-
解释器自动添加行位分号 行尾的分号对于C和Java来说必不可少,少了就报错。或许有人因此而转投Python使用行缩进来控制语句,这就需要游标卡尺了。
而JavaScript的解释器同html和css一样,并不严格,即使缺少行位的分号,也会自动加上。这可能带来意外的错误,使得代码结束于不该结束的地方。 -
弱类型语言 JavaScript是一种弱类型语言,其变量由解释器自动判断。而这无疑增加了运算的负担。
举个例子,== 和 === 运行的结果有时是不一样的,== 首先对变量进行类型转换,随后进行比较,而转换的结果常常出乎意料,不符合人类直觉。
"" == "0" // false
0 == "" // true
0 == "0" // true
false == "false" // false
false == "0" // true
false == undefined // false
false == null // false
null == undefined // true
" \t\r\n" == 0 // true
参考:
tips: Count Line of Code - 代码行数统计小工具
通过yarn安装: yarn global add cloc
在项目里: cloc --vcs=git .
浏览器的功能
-
发起请求 -> 下载HTML -> 解析HTML -> 下载CSS -> 解析CSS -> 渲染界面 -> 下载JS -> 解析JS -> 执行JS
-
用户界面,渲染引擎,JS引擎,储存等等 功能多分于不同线程
JS引擎
Chrome: V8(C++)
Node.js: V8(C++)
Firefox(netscape): SpiderMonkey(C++)
Safari: JavaScriptCore
Edge: Chakra(JavaScript)
IE: Chakra(JScript9)
Web API
MDN Web API参考: developer.mozilla.org/zh-CN/docs/…
浏览器提供runtime environmet,此时可调用WEB API,例如window, document.
执行环境和作用域
如图所示:
JavaScript 语法(ES6)
向前兼容,大小写敏感
表达式
表达式:zh.wikipedia.org/wiki/%E8%A1…
表达式,是由数字、算符、数字分组符号(括号)、自由变量和约束变量等以能求得数值的有意义排列方法所得的组合。约束变量在表达式中已被指定数值,而自由变量则可以在表达式之外另行指定数值。
e.g.
1+2;
console.long('hello world);
标识符
标识符只能包含字母或数字或下划线(“_”)或美元符号(“$”),且不能以数字开头。标识符与字符串不同之处在于字符串是数据,而标识符是代码的一部分。在 JavaScript 中,无法将标识符转换为字符串,但有时可以将字符串解析为标识符。
运算符
MDN: developer.mozilla.org/zh-CN/docs/…
运算优先顺序
括号→函数→乘方→乘、除→加、减→字符连接运算符→关系运算符→逻辑运算符
if...else...
if (condition1)
statement1
else if (condition2)
statement2
else
statementN
switch
switch (expression) {
case value1:
//Statements executed when the
//result of expression matches value1
[break;]
case value2:
//Statements executed when the
//result of expression matches value2
[break;]
...
case valueN:
//Statements executed when the
//result of expression matches valueN
[break;]
[default:
//Statements executed when none of
//the values match the value of the expression
[break;]]
}
三元表达式 | 条件运算符
condition ? exprIfTrue : exprIfFalse
符合condition,执行冒号前。不符合,执行冒号后。
短路逻辑
利用&&
statement1 && statement2 && statement3
任何一个为假则不执行,可替代if...else...
利用||
statement1 || statement2 || statement3
任意为真则执行
while loop
先检测再执行
while (condition)
statement
先执行再检测
do
statement
while (condition);
float导致的死循环
var a = 0.1
while (a !== 1){
console.log(a);
a = a + 0.1;
}
a永远不会等于1。
参考《JavaScript高级程序设计》 P28
float最高精度为17位小数,但其相加时的精确度远不如integer。
var a = 0.1;
var b = 0.2;
a + b == 0.3 ? true : false; //fasle
js执行a + b 的结果大约是0.30000000000000004。
for loop
for ([initialization]; [condition]; [final-expression])
statement
break, continue
break:跳出整个循环
continue:跳过本次循环,然后继续执行
JavaScript 数据类型
基本数据类型
(曾经)4基2空1对象
- number 数字
- string 字符串
- boolean 布尔
- symbol 符号
- undefined 未赋值(多用于定义空变量,非对象)
- null 空(可用于空对象)
- object 对象 (数组,日期,函数)
- BigInt: 新增数据类型(2020.06);BigInt 是一种内置对象,它提供了一种方法来表示大于 253 - 1 的整数。这原本是 Javascript中可以用 Number 表示的最大数字。BigInt 可以表示任意大的整数。
特殊值
正负0
+0 = -0 = 0 1 / 0 = infinity 1 / +0 = infinity 1 / -0 = -infinity
无限
infinity, +infinity, -infinity
无法表示的数字 NaN
not a number = NaN 本质是数字,只是无法被表示 NaN == NaN -> false
最大的数字/最小的数字
存储于 Number.MAX_VALUE 存储于 Number.MIN_VALUE
falsy值
5个相当于false但又不是false的值
undefined, null, 0, NaN, ''
类型转换
number -> string
- String(number);
- number + '';
string -> number
- Number(string);
- parseInt(string);
- parseFloat(string);
- 'string' - 0
any_variable -> boolean
- Boolean(variable);
- !!variable;
any_variable -> string
- String(variable);
- variable.toString;
关于js的奇葩问题:参考js 秘密花园
JavaScript 对象
{无序的数据}集合 | {键: 值}集合
写法
let obj1 = {'key1': value1, 'key2': value2};
let obj2 = new Object({'key': value});
console.log({'key': value});
切记,key必须也只能为字符串
即使不打引号编译器也会自动补上引号,但不打引号的key只能包含js标识符所允许的表达式,不能包含空格,也不能以数字为起始。
Object.keys(obj)可以得到obj所有的keys
将变量的值放入Object
let a = 'test';
let obj = {
[a]: 123
}
// 结果
obj -> {test: 123}
a -> "test"
关于属性
obj['name']
在obj中寻找有无键值为'name'的属性
obj.name
将name视为一个键值本身,直接查询。不接受string或者数字输入。
obj[name]
首先,在作用域中寻找有无一个名为name的变量,以该变量对应的值为key对obj进行查询,返回对应值
查看属性
查看属性名
Object.keys(obj);查看属性值Object.values(obj);查看属性名与值Object.entries(obj);查看自身属性+原型console.dir(obj);查看自身属性是否包含obj.hasOwnProperty('name');查看是否存在对应属性(自身+原型): -'key' in object变量对应的值是否是对象属性(自身+原型):
let a = 'key';
let obj = {'key':'value'};
a in obj;
# result
a in obj
< true
'key' in obj
< true
key in obj
< VM225:1 Uncaught ReferenceError: key is not defined
at <anonymous>:1:1
Delete 删除属性
删除对应属性:
delete obj.keydelete obj['key']
添加属性
obj.assign(key: value);
创建new object并附加属性
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
(这段我有点难以理解,感觉JavaScript的属性似乎都是附加在原型链上的?好像并不是依托class)
原型链参考:developer.mozilla.org/zh-CN/docs/…