笔记 - JS入门

154 阅读7分钟

关于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. (曾经)没有块级作用域 从C家族转换到js难免感到困惑,js没有块级作用域,在函数内部最接近的环境就是函数的局部环境。使用var定义的变量即为全局变量。
    ES6添加了letconst,多少解决了这个问题。

  2. 空对象 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的地方实在没有多少。

  1. 解释器自动添加行位分号 行尾的分号对于C和Java来说必不可少,少了就报错。或许有人因此而转投Python使用行缩进来控制语句,这就需要游标卡尺了。
    而JavaScript的解释器同html和css一样,并不严格,即使缺少行位的分号,也会自动加上。这可能带来意外的错误,使得代码结束于不该结束的地方。

  2. 弱类型语言 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.

执行环境和作用域

如图所示: image.png

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。 截屏2021-03-22 下午10.19.30.png 参考《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.key
  • delete obj['key']

添加属性

obj.assign(key: value);

创建new object并附加属性

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
(这段我有点难以理解,感觉JavaScript的属性似乎都是附加在原型链上的?好像并不是依托class)
原型链参考:developer.mozilla.org/zh-CN/docs/…