JS01 - JS简介/注释/折行、组成部分、创建方式、常用输出、变量声明、运算符、进制转换

247 阅读11分钟

JavaScript 简介

  1. 设计借鉴:C语言(基本语法)、Java语言(数据类型和存储管理)、Scheme语言(将函数提升到 first class 第一等公民的地位)、Self语言(使用基于原型 prototype 的继承机制)
  2. 演变历程:livescript (网景) ==> javascript(网景与Sun结盟) ==> ECMAscript(IE设计出JSscript后,欧洲协会统一语言标准)
  • 1994年,网景公司 Netscape 发布了 navigator 0.9 版浏览器,但只能浏览网页,无法交互。
  • 1995年,sun公司将Oak语言改名为Java,正式向市场推出,sun公司许诺这种语言可以“一次编写,到处运行(Write once, Run Anywhere)”,看上去很可能成为未来的主宰,网景公司动了心,决定与Sun公司结成联盟。
  • 1995年,34岁的系统程序员 Brendan Eich 在网景公司10天时间把JavaScript设计出来

JavaScript 注释(vscode)

  • 单行注释快捷键:ctrl + /
  • 多行注释快捷键:alt + shift + A

JavaScript 代码折行

可以在文本字符串中使用反斜杠对代码行进行换行,不影响输出内容

document.write("Hello \
world.");
  • 折行符号后面不能空格,否则报错;
  • 不能在文本字符串之外的代码行中使用,否则报错;
document.write \   //报错
("你好世界!");

JavaScript 组成部分

  1. ECMAScript本身是一个语言标准,因此也是JavaScript的核心(基本语法、数据类型)
  2. 文档对象模型(DOM)用于操作 HTML 和 XML 的节点视图
  3. 浏览器对象模型(BOM)对浏览器窗口进行访问和操作
  1. ECMAScript本身是一个语言标准,因此也是JavaScript的核心(基本语法、数据类型),描述了语言的基本语法(var、for、if、array等)和数据类型(数字、字符串、布尔、函数、对象等)
  2. 文档对象模型(DOM)用于操作 HTML 和 XML 的节点视图。DOM 将把整个页面规划成由节点层级构成的文档,HTML 或 XML 页面的每个部分都是一个节点的衍生物。
  3. 浏览器对象模型(BOM)对浏览器窗口进行访问和操作。例如弹出新的浏览器窗口,移动、改变和关闭浏览器窗口,提供详细的网络浏览器信息(navigator object),详细的页面信息(location object),详细的用户屏幕分辨率的信息(screen object),对cookies的支持等等。BOM作为JavaScript的一部分并没有相关标准的支持,每一个浏览器都有自己的实现,虽然有一些非事实的标准,但还是给开发者带来一定的麻烦。

JavaScript 创建方式

  1. 行内式:直接把代码写在标签上
  2. 内嵌式:把代码书写在一个 script 标签内
  3. 外链式:把代码书写在一个 .js 文件内(最常用)
  1. 行内式:直接把代码写在标签上,链接用 href属性,非链接用 事件属性
    • a 标签:书写在 href 属性上。<a href="javascript:console.log(123);for(var i=0;i<3;i++){console.log(i)}">click</a>
    • 非 a 标签:书写在事件属性上。<div onclick="javascript:console.log(123);for(var i=0;i<3;i++){console.log(i)}">click</div>
  2. 内嵌式:把代码书写在一个 script 标签内,最好放在</body>
    • 内嵌式 JS 代码, 不需要依赖任何行为, 打开页面就会执行,注意 script 标签可以放在head中,也可以放在body中,但最好放在</body>前,因为script块执行是从上到下的,如果script标签块后面还有代码,会因为script还没执行完,而被卡住。
    • <script>
          alert('click result');
      </script>
      
  3. 外链式:把代码书写在一个 .js 文件内(最常用)
    • 外链式 JS 代码, 不需要依赖任何行为, 打开页面就会执行,使用 script 标签的 src 属性引入指定 JS 文件 <script src="./test.js"></script>
    • 注意:如果是引入 <script src="./test.js"></script> 的情况,在script标签内部添加内容将不会被解析,例如:<script src="./test.js">alert("result")</script>中的alert("result")将不会被解析

JavaScript 常用输出

  1. 警告弹窗 alert()
    • 无返回值,输出为string
    • 支持转义字符、数学运算符,通过+号拼接字符串
    • alert("Hello\nworld\n"+(2+3)+" times");
  2. 确认弹窗 confirm()
    • 点击"确认",返回 true;点击"取消",返回 false
    • confirm("Are you sure?");
  3. 提示输入 prompt()
    • 点击"确认",返回输入框内容;点击"取消",返回null
    • prompt("提示信息","输入框默认内容/可修改")
    • prompt("提示信息/请输入") 不设定输入框内容默认为空字符串
  4. 控制台 F12
    • 控制台打印 console.log([content]); content 本身是什么类型,在控制台输出的就是什么类型,一般用于开发
    • 如果打印多个,用逗号隔开 console.log("one","two",variable)
  5. 写入页面
    • 覆盖整个页面 document.write([content]);
    • 覆盖指定内容 [element].innerHtml/innerText
    • 修改制定表单 [input].value

JavaScript 变量

  • 变量:在程序中保存数据的一个 容器
  • 变量名:存储数据的 标识符,根据变量名可以获取到内存中存储的数据
  • 声明语法:旧方案 var/function、新方案 let/const/import

命名规范

  1. 数字/字母/下划线:变量能以字母(大小写敏感)$_ 符号开头,数字不能是变量名的开头,否则报错;一般约定:美元号$开始的变量一般用来存储JQ获取的值,下划线_开始的变量一般代表全局或公共的
  2. 驼峰命名法:第一个单词首字母小写,其余每一个有意义的单词的首字母大写,语义化要明显,少使用 a/b/c/d/x... 等无意义的字母
  3. 匈牙利命名方式(变量名+对象描述):整数i 浮点fl 布尔b 字符串s 数组a 对象o 函数fn 正则re,例如:iAge = 18;
  4. 不能使用保留关键字:null是关键字,但 undefined 不是保留关键字,但也不建议使用
  5. 声明多个变量:一条语句声明多个变量,使用逗号分隔,可跨行,但不能同时赋一个值,例如 let x,y,z=1;x,y 为 undefinedz 为 1
  6. 重新声明变量:如果变量使用 var 声明且已经赋值后,再次声明,该变量值不会丢失。例如:var carname="Volvo"; 后再 var carname;,结果依旧是 Volvo

var 变量-函数作用域

  • 作用域:函数作用域,通过 var 关键字声明的变量,作用域是整个封闭函数,不是单独的块级,因此就算在函数内部有多个语法块,那么也不会受到语法块的限制
  • 变量提升:var关键词声明的变量可以先使用,后声明,但如果先使用是undefined
  • 重复声明:允许
!function(){
    console.log(i);
    for(var i=0;i<3;i++){
        console.log(i); 
    }
}()
//undefined 012

let 变量-块级作用域(ES6)

  • 块级作用域:let 声明的变量,受到语法块的限制
  • 变量提升:不允许,let 必须先定义再使用
  • 重复声明:不允许
  • 暂时性死区(temporal dead zone, TDZ):在一个块级作用域内,如果存在有 let 声明的变量,那么该变量自动绑定当前的作用域,此时在该作用域内,使用这个名字的变量只用是 let 声明的,就像个“地头蛇”
<button>0</button>
<button>1</button>
<button>2</button>
<script>
    var btns = document.getElementsByTagName("button");
    /* var 函数作用域 VS let 局部作用域 */
    //var 函数作用域
    for (var i = 0; i < btns.length; i++) {
        btns[i].onclick = function () {
            console.log(btns[i].innerText);
        }
    }
    //上面代码的输出结果是  TypeError: Cannot read properties of undefined (reading 'innerText')
    //因为 i 是 var 声明的,var 是函数作用域,因此每次循环的时候 i 都还是同一个变量,值被覆盖了,
    //由此,虽然事件绑定是有序的(这就是可以使用this解决的原因),但回调函数中还使用 btn[i],就只能输出最后一个 i,导致越界报错
    // 过程类似下列代码:
    var m = 0;
    btns[m].onclick = function () {
        console.log(this.innerText);    // 0 -> 绑定的元素对象没问题
        console.log(btns[m].innerText); // 2 -> 回调函数还使用btn[m],就只能输出最后一个了
    }
    m = 1;
    btns[m].onclick = function () {
        console.log(this.innerText);    // 1
        console.log(btns[m].innerText); // 2
    }
    m = 2;
    btns[m].onclick = function () {
        console.log(this.innerText);    // 2
        console.log(btns[m].innerText); // 2
    }

    //let 局部作用域 -> 如果使用let局部作用域,那么 i 就会 限定在各自的块中,因此回调函数的调用能够互不干扰
    for(let j = 0; j < btns.length; j++){
        btns[j].onclick = function(){
            console.log(btns[j].innerText); // 0 1 2
        }
    }
</script>
//暂时性死区
var temp = "temp";
console.log(temp);  //temp 能够正常输出
if(true) {
    temp = "new"; //暂时性死区 -> ReferenceError: Cannot access 'temp' before initialization
    let temp;
}

const 常量-块级作用域(ES6)

  • 块级作用域、变量不提升、重复声明:同 let
  • 只能修改对象属性:基本数据类型不允许修改,但如果是对象,其属性可以修改,但不能修改地址值;
  • 声明必须赋值:如果const只声明,不赋值,则相当于是赋值了一个undefined,而这种操作不被ES6允许,因此,会报错
/*const 声明*/
const a = 0;			//定义常量 a 并赋值为0;
a=1;				//报错(不能重新赋值)
const a = 2;			//报错(不能重新赋值)
console.log("a is "+a);	//a is 0
//声明常量,块级作用域
function constTest(){
    const x = 1;
    if(true){
        const y=2;        
        console.log(x);     //1
    }
    console.log(y);         //Uncaught ReferenceError: y is not defined
}
//不允许:只声明,不赋值
num = 5;
const num;  //语法报错
//对象:不可修改地址值,可以修改属性值
const objJohn = {sname:"John",age:48}
const objJohnson = {sname:"Johnson",age:20};
objJohnson = objJohn;       //Uncaught TypeError: Assignment to constant variable.
objJohn.sname = objJohnson.sname;
console.log(objJohnson);    //{sname: 'Johnson', age: 20}

无关键字-变量-window属性

  • window 属性:不使用关键字,会被当做window的一个属性
  • delete 删除:需要用delete 操作符用于删除对象的某个属性
//var 关键字会覆盖window属性=>相当于 var 的变量提升
num = 11       //window的属性
var num = 10    //全局变量 => 覆盖window属性/变量提升
console.log(num);

window.str = "James";
console.log(str);   //报错=>暂存行死区
let str = "Joker";
console.log(str);

JavaScript 运算符

  1. 基本运算符+ - * / % ++ -- = += -= *= /= %=,例如 5%2 结果为 1,注意 模运算(取余) 的结果符号只与左边值的符号有关,例如 -5%2 结果为 -15%-2 结果为 1;只有加号两边有字符串的时候才是字符串拼接,- * / % 都是数学运算
  2. 条件运算符/三目(元)运算符,“目(元)”是指参与运算的对象(值/变量)的个数,表达式为 [关系表达式] ? [执行语句1] : [执行语句2],执行时,先对关系表达式进行计算,如果计算结果为Ture 则执行语句1 并返回运算值,如果执行结果为False 就执行语句2 同时也返回运算值,例如 console.log(25>35?"25>35":"25<35") 结果为 25<35
  3. 逻辑运算符&& || !,注意 && 优先级高于 ||,例如 15||14 && 12||915逻辑或:前一个表达式的条件为真就会发生短路运算,不再执行后面的表达式。逻辑与: 前一个表达式的结果为假就会发生短路运算,不再执行后面的表达式。
  4. 位运算符& |,参与运算的元素会先转换为二进制代码,然后再进行运算
console.log(true | false)   //1
/**true  ->(to binary) 1
 * false ->(to binary) 0
 *       ->按位或      1
 */
console.log(true | true)    //1
/**true  ->(to binary) 1
 * true  ->(to binary) 1
 *       ->按位或      1
 */

console.log(false & false)  //0
/**true  ->(to binary) 1
 * false ->(to binary) 0
 *       ->按位并      0
 */
 console.log(2 | 3)          //3
/**2  ->(to binary) 01
 * 3  ->(to binary) 11
 *    ->按位或      11
 */
 console.log(7 & 8)          //0
/**7  ->(to binary) 0111
 * 8  ->(to binary) 1000
 *    ->按位并      0000
 */

JavaScript 进制转换

  1. 二进制转换为十进制:求和 [number]*2^[numbers of 0]
转换二进制1001011为十进制
//------------------
1000000 --> 1*2^6=64 --> 2^6=64
0001000 --> 1*2^3=8  --> 2^3=8
0000010 --> 1*2^1=2  --> 2^1=2
0000001 --> 1*2^0=1  --> 2^0=1
//------------------
1001010 --> 2^6+2^3+2^1+2^0=64+8+2+1=75
转换十进制1001011为十进制
//------------------
1000000 --> 1*10^6=1000000 
0001000 --> 1*10^3=1000  
0000010 --> 1*10^1=10  
0000001 --> 1*10^0=1  
//------------------
1001010 --> 10^6+10^3+10^1+10^0=1000000+1000+10+1=1001011
  1. 二进制转换为八进制:求和 [number]*8^[numbers of 0]
转换二进制1001011为八进制
//------------------
1000000 --> 1*8^6=262144
0001000 --> 1*8^3=512 
0000010 --> 1*8^1=8  
0000001 --> 1*8^0=1 
//------------------
1001010 --> 8^6+8^3+8^1+8^0=262144+512+8+1=262665
  1. 二进制转换为十六进制:求和 [number]*16^[numbers of 0]
转换二进制1001011为八进制
//------------------
1000000 --> 1*16^6=16777216
0001000 --> 1*16^3=4096 
0000010 --> 1*16^1=16  
0000001 --> 1*16^0=1 
//------------------
1001010 --> 16^6+16^3+16^1+16^0=16777216+4096+16+1=16781329
  1. 十进制转换为二进制:除2取余,逆序排列
转换十进制894为二进制
//------------------
894/2=447...0
447/2=223...1
223/2=111...1
111/2=55 ...1
55/2=27  ...1
27/2=13  ...1
13/2=6   ...1
6/2=3    ...0
3/2=1    ...1
1/2=0[止]...1
//------------------
逆序 --> 0011 0111 1110
  1. 任意转换 - 对位法

十进制147换算.png 十进制965换算.png 十六进制F7换算.png

  1. 二进制逻辑运算符
&  AND:同 1  1 ,否则为 0 
--> 
272 & 147 = 0001 0001 0000 & 0000 1001 0011 = 0000 0001 0000 = 16(十进制)
|  OR:有 1  1 ,否则为 0 
--> 
272 | 147 = 0001 0001 0000 | 0000 1001 0011 = 0001 1001 0011 = 403(十进制)
^ 异或 XOR:相同为 0 ,否则为 1 
--> 
272 ^ 147 = 0001 0001 0000 ^ 0000 1001 0011 = 0001 1000 0011 = 387
5 ^ 6 = 0101 ^ 0110 = 0011 = 3
其它:NAND与非 XNOR异或非
<< 左移Lsh:按照2进制向左移动两位
-->
35<<2 = 0010 0011 << 2 = 1000 1100 = 140
>> 右移Rsh:按照2进制向右移动两位
-->
35>>2 = 0010 0011 >> 2 = 0000 1000 = 8