JavaScript (快速入门篇)


\(updata : 2022.1.15\)

学习的原文

\(① :\) 基本语法

\(JavaScript\) 每个语句以 ";" 结束 ,语块用{...} ; 但并不强制要求每句都加上 ";" , 负责执行代码的引擎会自动帮助添上的;(但有时候自动补全会违背本意,建议一定要加上);

"//" , "/**/" 的注释效果在这里同样适用;


\(② :\) 数据类型和变量

感觉 \(c\) 里面的类型在这里都有提 , 但有好多的细节不同;

比如既可以用 ' ' 又可以用 " " 来引用字符串 ;

比较用等于的时候不是 " == " , 而是 " === " , 原因廖雪峰老师也着重强调了 , "==" 会转化数据类型再比较,往往会有诡异的结果;而"===""只要数据类型不相同就返回 \(false\) , 数据类型相同再进行比较;

不过最令我吃惊的是在 \(JavaScript\) , 作为动态语言 \(var\) 的存在

var a = 12 ; true ; "ABC" //整形,布尔,字符均可;

var a = 12 ;
a = "ABC" //还能变化不同的类型

var a = [1,2,'ABC',null,true,undefined];
//同一数组里面也有不同的类型
//跟c一样,同下标来访问数组元素,例如 a[0] //1 ,a[2] // 'ABC';
//文章还提及另一种数组创建方式
new Array(1,2,3) //创建数组[1,2,3] , 强烈建议使用[];

var a = [1,[3,4,5],[6,7,8]];
//访问 4 的话 , a[1][1];

var xiaoming ={
	name : '小明',
	age : 18
}
//看上去像结构体 , 这里称作为对象;
//其中xiaoming称为对象变量 , name称为属性名 ,'小明'称为属性; 

在这里我们看到了 \(var\) 的重要性,同一变量可以反复赋值,而且可以是不同类型的变量,但注意 \(var\) 只能申明一次;如果一个变量没有通过 \(var\) 申明就被使用,那它自动变为全局变量,同一页面的不同\(JavaScript\) 文件中都使用同一全局变量的话,造成变量的相互影响,产生难以调试的结果,建议在代码的第一行写下 'use strict' , 这样就开启了\(strict\) 模式,强制用 \(var\) 声明变量 , 否则报错.

同时我们注意到在举例时出现 \(null\) , \(undefined\) , \(null\) 代表一个空的值, \(undefined\) 代表未定义 , 之后在字符串,数组那块每当函数(应该是函数)调用超越范围时往往会出现 \(undefined\) ; 文章还提及了 \(NaN\) , 意为 not a number 当无法计算结果时候用的 , 这里注意 \(NaN\) 不与任何数相同 ,包括它自己 ,只能通过 isNaN()函数判断, isNaN(NaN) // true ; \(Infinity\)表示无限大;

还有一个很重要的一点就是变量名由英文大小写,数字,\(\$\)\(\_\) 的组合,且数字不能开头;


\(③ :字符串\)

前面提及了字符串用 " "' ' 引用 ;
如果 ' 也是一个字符的话 ,用 " "括起来 ,例如 "I'm ok!".
那么如果既有 ' 又有 " , 这个时候我们就要用转义字符 '\' , 例如' I\'m \"小明\" ',就有了I'm "小明".的效果.

如果转义字符本身也要转义,\\即可,同样'\n'代表着换行;

文章还提及多行字符串

怎么输出

多行
字符串
测试

这三行的效果?

① console.log('多行') ; console.log('字符串') ; console.log('测试');
//console.log()每次输出自动换一行;

② console.log('多行\n字符串\n测试');
//主要利用到了'\n'的操作;

③ 利用反引号
console.log(`多行
字符串
测试`);

模板字符串

//把多个字符串相连,可以用 '+' 号;
//例如;
var name = 'xiaoming';
var age = 20;
var message = '你好, ' + name + ', 你今年' + age + '岁了';
console.log(message);

//当很多变量需要连接时,利用 `` 则会更加便捷
//同样上面那个例子:
var message = `你好,${name},你今年${age}岁了!`;
//我们称此为模板字符串;

操作字符串

s.length //表示一个函数返回值,返回字符串的长度,注意没有();

var s = "a b c";
s[0] // 'a' , s[1] // ' ' , s[2] //'b';
//注意当s[5]越界时返回的是undefined;

//原文特别强调了字符串是不可变的,对字符串的某个位置赋值不会错但没有用
//例如
var s = "cde";
s[0] = 'a';
console.log(s) ; //仍然为cde;
console.log(s[0]) ;//仍然为c;

//但是再整体赋值就可以整体改变;
s = "abc";
console.log(s);//s为abc;

s.toUpperCase();//同样是一个返回值,把一个字符串全部变成大写 ; 注意有();

s.toLowerCase();//返回值;把一个字符串全部变成小写;

s.indexOf('字符串') //, 类似于find函数 ,返回s中被找字符子串的首位置 , 不存在的话会输出-1;

s.substring(0,5) ; 返回s中[0,5),不包括5的字符子串 ; s.substring(7)  , 从7开始到结束.
如果区间不存在,或者有一部分区间不存在 , 则不会输出不存在的部分;


\(④ :\) 数组


arr.length;//数组的长度  
直接给长度赋值会导致原数组数组改变:少了的补 undefined ,多出的截去;
//var arr = [1,2,3] ; arr.length 由3->2->3 , arr为[1,2,empty],并不会恢复;

直接通过索引(下标)对数组赋值则会改变对应下标的值 , 这点与字符串是不一样的;
如果索引超过了原数组的大小 , 举个例子
var a = [1,2,3,4,5] ; 
a[6] = 'x';
//此时a = [1,2,3,4,5,undefined,'x'];
//不过undefined的位置输出什么都没有;

与string类似 ,Array也可通过indexOf()来搜索指定元素的位置,二者是相通的;

slice()对应String的substring()版本 , 当括号内不带有任何参数时 , 它默认全体复制 ,话说substring()也有这个效果; 

push()操作在数组的尾部添加 , unshift()在数组的头部添加;
二者会返回数组的实时长度 ;
push,unshift能一次增加多个元素;//arr.push(1,2) , arr.unshift(1,2,3);

pop()去除最后一个元素 , shift()去除开头元素;
二者都返回删除元素,如果没有元素还删除,就返回undefined;

arr.sort();//排序,从小到大;

arr.reverse();//反转;

接下来就是大家都说好的 splice()

arr.splice(1,2,'a','b');
从下标1开始删除2个元素 (别与slice的区间弄混了) , 再从下标1开始添加'a','b';
arr.splice(1,0,'a','b'); //只添加;
arr.splice(2,1);//只删除;

要注意slice与splice的区别 , slice是截取一段返回新的array(也就截取值)但对原数组并没有修改 , 而splice是在原数组上修改 , 返回删除值(好吧也就是截取值);

concat() //把一个Array与另一个Array连接起来,并返回一个新的Array(其实是接受到任意个元素和Array,将它们拆开再连接起来) ;
var a = [1,2,3] ,t = [7,8,9];
added = a.concat(t,['A','B','c'],1,2,3);//注意它是一个返回值;

join('字符串');//把当前Array的每个元素用指定的字符串连接起来,注意是返回值;
var a = [1,2,3] ; 
var changed = a.join('-');

多维数组
var arr = [[1,2,3],[400,500,600],'-'];
注意[1,2,3]整体为第0维 , 以此类推;
arr[0][1] // 2;
arr[1][1] // 500;
arr[2] = '-';

目前多维数组配合splice在单维上仍有效,多维就有点尬;
//arr.splice(a[0][1],1,"A"),arr仍未变;


对象

是一种无序的集合数据类型 , 有\(c\)中的结构体的感觉,它由若干个键值组成;

var xiaoming={
	name : '小明',
	birth : 1990
}
//最后一个键值不用加',' ;
//上述对象声明一个'name'属性 , 值是'小明';
//一定是var xiaoming = {}; '='不能漏掉了.

xiaoming.name //'小明';
xiaoming.birth //1990;

属性名字含有特殊字符的时候,就必须用'' ,访问时用[]
'middle-school' : 'No.1 school'
xiaoming['middle-school'];

访问一个不存在的属性时会返回 undefined;

可自由的添加与删除(直接引用原文)
var xiaoming = {
    name: '小明'
};
xiaoming.age; // undefined
xiaoming.age = 18; // 新增一个age属性
xiaoming.age; // 18
delete xiaoming.age; // 删除age属性
xiaoming.age; // undefined
delete xiaoming['name']; // 删除name属性
xiaoming.name; // undefined
delete xiaoming.school; // 删除一个不存在的school属性也不会报错

检验一个对象是否有某种属性时
用 in 或 hasOwnProperty()函数;

'name' in xiaoming // true;

但最好用后者 , 原文指出 in 判断的属性存在可能对象并没有,是他继承得到的;

xiaoming.hasOwnProperty('toString')//false

\(⑤ :\) 条件判断

这玩意就是 \(if\) , \(else if\) , \(else\) ,应该会.


\(⑥ :\) 循环

for(;;) // 与 $c$ 一样 ; $break$ 也可以用;

for 的 一个变体是for in , 用来遍历对象
var o = {
    name: 'Jack',
    age: 20,
    city: 'Beijing'
};
for (var key in o) {
    console.log(key); // 'name', 'age', 'city'
}

同时 Aarry 也可以用for in
var a = [1,2,3,4];
for(var i in  a){
	console.log(a[i]);
}
原文强调了for...in对Array的循环是String而不是Number

while 与 do...while的用法则是与c一致的;

同样a[++i] , a[i++]的优先级照样适用;

\(⑦ : Map 与 Set\)

跟c++里面的stl中的map,set差不多.

map强调一种 "key-value" 的映射关系;
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95

var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined

var m = new Map();
m.set('Adam', 67);
m.set('Adam', 88);
m.get('Adam'); // 88

我们注意到m.set(key,value) ; 
get查询,delete删除时得用key为关键字查询/删除value;
同时注意delete在删除对象得属性时也出现过.

set则只存key,不存value,且没有重复的key;
var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3

var s = new Set([1, 2, 3, 3, '3']);//注意Map是[]里面加[];
s; // Set {1, 2, 3, "3"},重复元素剔除;

s.add(4);
s; // Set {1, 2, 3, 4}
s.add(4);
s; // 仍然是 Set {1, 2, 3, 4}

var s = new Set([1, 2, 3]);
s; // Set {1, 2, 3}
s.delete(3);
s; // Set {1, 2}

\(⑧ : iterable\)

因为遍历 Map 与 Set 无法使用下标,为了统一集合类型,ES6标准引入了iterable类型 , 对array , Map , Set均适用

var a = [1,2,3];
var b = new Map([4,5,6]);
var c = new Set([1,'x'],[2,'y'],[3,'z']);

遍历时我们要用到 for...of... ( 与for...in...做区别 )
var a = ['A', 'B', 'C'];
var s = new Set(['A', 'B', 'C']);
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);

for(var x of a){
	console.log(x) ; // 'A','B','C';
	//注意这里和for...in是有区别的 , 如果是for...in , 那么这里的x就是下标0,1,2;
}
for(var x of s){
	console.log(x);//'A','B','C';
}
for(var x of s){
	console(x[0] + '=' + x[1]); //1 = x , 2 = y ,3 = z;
}

这里与for...in做区别 , for...in遍历对象的属性名称,一个Array数组实际上也是一个对象,每个元素索引被看成一个属性

var a = ['A', 'B', 'C'];
a.name = 'Hello';//手动添加额外属性
for (var x in a) {
    console.log(x); // '0', '1', '2', 'name'
}

但for...of只循环集合本身元素;
var a = ['A', 'B', 'C'];
a.name = 'Hello';
for (var x of a) {
    console.log(x); // 'A', 'B', 'C'
}

文章提及更好的遍历方法
iterable内置的forEach

var a = ['A','B','C'];
a.forEach(function (element,index,array){
	// element: 指向当前元素的值
    // index: 指向当前索引
    // array: 指向Array对象本身
    console.log(element + ', index = ' + index);
	/*A, index = 0
	  B, index = 1
	  C, index = 2*/
}); (一定注意这里是有回括号的);
//element,index,array仅仅是名称而已,可替换,原文应该是想规范变量,并且可适当舍弃参数.

var s = new Set(['A','B','C']);
s.forEach( function(element,sameElment,set){
   console.log(element);//这个函数对set与array的参数是一样的(好吧,有一点不同),但set是没有索引的,前两个都是元素.
});

var t = new Map([1,'x'],[2,'y'],[3,'z']);
t.forEach( function(value,key,map){//参数前两者对应了value和key;
	console.log(value);
});

前面说的舍弃参数,只能按从后往前的顺序舍弃.