JavaScript 是属于网络的脚本语言!JavaScript被数百万计的网页用来改进设计、验证表单、检测浏览器、创建cookies,以及更多的应用。
JS语言的特点
- 解释性语言(不需要编译成文件)跨平台
- 单线程
ECMA标注-为了取得技术优势微软退出了JScript,CEnvi推出ScriptEase,与JavaScript同样可在浏览器上运行。
为了统一规格JavaScript兼容于ECMA标准,因此也成为ECMAScript
(一)解释性语言与编译性语言的区别
编译性语言:
程序在执行之前需要一个专门的编译过程,把程序编译成为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高,依赖编译器,跨平台性差些。如C、C++、Delphi等解释性语言:
解释型语言的程序不需要在运行前编译,在运行程序的时候才翻译,专门的解释器负责在每个语句执行的时候解释程序代码。这样解释型语言每执行一次就要翻译一次,效率比较低。如JavaScript php python等
java有点特殊他是先通过一个指令javac-->编译-->.class文件在通过 --->jvm虚拟机--解释执行 graph TB
A{java文件}-->B(通过指令javac)
B--> C[编译成.class]
C--> D[jvm虚拟机]
D--> E[解释执行]
(二)单线程
单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。
JS的执行队列
轮转时间片:时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法,又称RR调度。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
可以这样子说JS引擎要执行任务A或者任务B,它不会把任务A执行完之后再执行任务B,而是会把任务A和任务B切成以毫秒或者微秒为单位的片段,在把这些片段按照随机的方式进行排列(这个也叫争抢时间片),在把这些排列好的片段一个一个往JS引擎里面去送,在JS引擎以一个一个片段为基准单位去执行这个片段,把任务A和任务B按照片段是执行完
主流浏览器有哪些
主流浏览器 | 内核 |
---|---|
IE | trident |
Chrome | webkit/blink |
firefox | Gecko |
Opera | presto |
Safari | webkit |
如何引入JS?
- 页面内嵌<script type=
text/javascript
></script>标签 - 外部引入<script src=
location
></script>
为符合Web标准(w3c标准中的一项)结构、样式、行为相分离,通常会采用
外部引用
结构 | 行为 | 样式 |
---|---|---|
html | js | css |
相分离: 三个文件分开写,开发标准
JS基本语法
变量(variable)声明、赋值分解
//单一var变量声明
var 变量名
命名规则
- 变量名必须以英文字母、_、$开头
- 变量名可以包括字母、_、$、数字
- 不可以用系统的关键字、保留字作为变量名
- 查询关键或者保留字
声明变量
//声明一个变量var a;
//声明多个变量var a = 100,
b = 200,
c = "holle world";
基本语法
- 原始值(栈数据):Number Boolean String undefined null(不可改变的原始值)
- 引用值(堆数据):array Object function date RegExp等
//原始值几种类型
//Number类型,数字类型
var a = 111;
//String类型,字符串类型
var b = "我超级帅"
//Boolean类型 就两个值 一个true 一个false
var c = true;
var d = false;
//undefined类型 就一个值 unedfinedvar
e = unedfined;//null类型 表示站位 表示空
计算机在存值的时候把原始值和引用值存的地方不一样原始值存到栈里面(stack),引用值大致存到堆里面(heap);
js栈和堆的区别
//原始值 栈
var a = 10,var b = a;
a = 20; //打印出b-->10;
//引用值 堆
//例子一
var arr = [1,2];
var arr1 = arr;
arr.push(3);//打印出arr1-->[1,2,3];
//例子二
var arr = [1,2];
var arr1 = arr;
arr = [1,3];//打印出arr1-->[1,2];
JS语句基本规则
- 语句后面要用分号结束
;
。 - JS语法错误会引发后续代码终止,但不会影响其它JS代码块
- 书写格式要规范,“=、+、-、/”两遍应该有空格
- function demo(){}函数后面可以不用加分号,
- for(){}后面可以不用加分号,
- if(){}后面可以不用加分号
//JS语法错误会引发后续代码终止,但不会影响其它JS代码块
<script type="text/javascript">
console.log(a);
<script>
<script type="text/javascript">
var b = 1;console.log(b);
<script>
第一部分代码块会直接报错,但是不影响第二部分的打印b;
JS运算符
- JS的Numbr默认浮点型
运算操作符
- 数学运算、字符串链接
任何数据类型加字符串都等于字符串
+
、 “-”、“”、“/”、“=”、“()” “++”、“--”、“+=”、“-=”、“/=”、“=”、“%=”;- 优先级“最弱”,“()”最高
JS的加减乘除
//加
var a = 10,
b = 20,
c,
e,
g,
h,
f;
c = a + b; //加 打印30
f = 1 + "a"; //字符串拼接 打印"1a"
e = b - a; //减 打印20
g = a*b; //乘 打印200
h = b/a; //除法 打印2
var num = 0/0 //打印NaN全称Not A Number计算机知道是数字但是显示不出来用NaN表示 叫非数 是Numbar类型
var a = 10;
var b = 10;
a++; //打印a-->11;
b--; //打印b-->9;
a += 10; //等于a = a + 10,打印a-->20
a += 10 + 1; //等于a = a + 10 +1 打印a-->21
//*=、/=相似 不做介绍
比较运算符、逻辑运算符
比较运算符
“>”、“<”、“==”、“>=”、“<=”、“!=”
- 比较的结果为boolean值
逻辑运算符
“&&”、“||”、“!”
- 运算结果为真实的值
- 被认定为false的值
undefined、null、NaN、""、0、false
// 比较运算符
var a = 10,
b = 20,
c;
c = a < b; //true
c = a > b; //false
c = "a" > "b" //false,比较的ascll码值
c = a == b // false
c = a != b //false
c = NaN == NaN //false,NaN不等于任何数;
//逻辑运算符
//逻辑与 &&
var a = 1 && 2 //打印出a-->2
//逻辑或 ||
var a = 1 || 3; //打印出a-->1
//逻辑非 ! 转换成布尔值在取反
var a = !123; //false;
- 逻辑与 && 先看第一个表达式转换成布尔值的结果,如果为真,那么它会看第二个表达式转换为布尔值的结果,然后如果只有两个表达式的话,只看第二个表达式,就可以返回该表达式的值了,一种
短路
语句;
条件语句,循环语句
条件语句
- if语句
- if else
Switch语句
- switch
循环语句
- for
- while
- do....while
if(条件){
//条件为真时执行的方法
}
if(条件){
//条件为真时执行的方法
}
else{
//条件为假的时候执行的方法
}
if (条件 1){
当条件 1 为 true 时执行的代码
}
else if (条件 2){
当条件 2 为 true 时执行的代码
}
else{
当条件 1 和 条件 2 都不为 true 时执行的代码
}
for (语句 1; 语句 2; 语句 3){
//被执行的代码块
};
for (var i=0;i<10;i++){
document.write(i);
}
while(条件){
//需要执行的代码
}
var i;
while(i<10){
document.write(i);
i++;
}
do{
需要执行的代码
}while (条件);
do{
document.write(i);
i++;
}while (i<5);
switch(n){
case 1:
执行代码块 1
break;
case 2:
执行代码块 2
break;
default:
n 与 case 1 和 case 2 不同时执行的代码
}
var n = "a";
switch(n){
case "a":
console.log("a");
break;
case 2:
console.log("b");
break;
case true:
console.log("c");
break;
case 3:
console.log("d");
break;
}
打印出来
a、b、c、d
可以用break,来终止循环,只能写在循环里面;
continue,终止本次循环,进行下次循环。
数组或对象 typeof、类型转换
数组
数组对象
用来在单独的变量名中存储一系列的值。- 形式是一个中括号,里面可以写很多东西,中间用逗号隔开,每个逗号类似可以隔开两个仓库,每个仓库可以放东西,比如Number,String,undefined,放什么类型的值都可以。
var arr = [1,2,undefined,"abc",["a",1,5],null]
//数组的增加
document.write(arr.push("吴彦祖")); //打印arr-->[1,2,undefined,"abc",["a",1,5],null,"吴彦祖"]
//数组的删除
arr.splice(0,1) //打印出来是[2,undefined,"abc",["a",1,5],null]
//数组的修改
arr[0]; //打印出来是0
arr[0] = 100; //在打印出来就是100;
//数组的查看
for(var i = 0;i<arr.length;i++)
console.log(arr[i]);
关于数组的其他方法
- For...In 声明
使用 for...in 声明来循环输出数组中的元素。 - 合并两个数组 - concat()
如何使用 concat() 方法来合并两个数组。 - 用数组的元素组成字符串 - join()
如何使用 join() 方法将数组的所有元素组成一个字符串。 - 文字数组 - sort()
如何使用 sort() 方法从字面上对数组进行排序。 - 数字数组 - sort()
如何使用 sort() 方法从数值上对数组进行排序。
对象
- JavaScript 对象是拥有属性和方法的数据。
- JavaScript 中的所有事物都是对象:字符串、数字、数组、日期,等等。
- 在 JavaScript 中,对象是拥有属性和方法的数据。
var obj = {
key : value,
建 : 值,
属性: 属性值;
}
var car = {
type:"Fiat",
model:500,
color:"white"
money:undefined,
newCar:false,
}
//增加对象属性
car.width = "1.6m";
//删除对象属性
delete.car.width;
//修改对象属性
car.width = "1.5m";
编程形式
- 面向过程
- 面向对象
typeof
六钟数据类型
- Number
- string
- boolean
- undefined
- object
- function
//第一种方法
//typeof("里面放数据")
var num = 123;
var str = "123";
var a = true;
var b = null;
var c = undefined;
console.log(typeof(num)); //打印-->number
console.log(typeof(str)); //打印-->string
console.log(typeof(a)); //打印-->boolean
console.log(typeof(b)); //打印-->object
console.log(typeof(c));//打印-->undefined
//第二种方法
console.log(typeof c); //打印-->undefined 不需要括号,空格也可以
类型转换
显示类型转换
- Number(mix)
- parseInt(string,radix)
- parseFloat(string)
- toString(radix)
- String(mix)
- Boolean()
//Number转换成数
var num = Number("123");
= true;
= false;
= null;
= undefined;
= "a";
= "123abc";
console.log(num)//打印123
//打印1
//打印0
//打印0
//打印NaN
//打印NaN
//打印NaN
//parseInt转换成整数
//parseInt(String,radix)
//radix 是调整进制取值范围是2-36
//parseInt 是用数字为一直往后面看,看到截止,一直看到非数字位截止,把之前的数字返回
var num = parseInt("123.9");
= true;
= false;
= null;
= undefined;
= "a";
= "123abc";
console.log(num) //打印123不是四舍五入
//打印NaN
//打印NaN
//打印NaN
//打印NaN
//打印NaN
//打印123
//parseFloat
//把数字转换为浮点数
var num = parseFloat("123.9");
= true;
= false;
= null;
= undefined;
= "a";
= "123.2abc";
console.log(num)//打印123.9
//打印NaN
//打印NaN
//打印NaN
//打印NaN
//打印NaN
//打印123.2
//String把内容换成字符串
var num = String(123.9);
= undefined;
console.log(num)//打印"123.9"
//打印"undefined"
//Boolean转换成布尔值
//除了undefined、null、NaN、""、0、false 打印出来的是false以外, 其他的全是true
var num = Boolean(123.9);
= undefined;
console.log(num) //打印true
//打印false
//toString(radix) 转换成为字符串
//两个不能用一个undefined一个null会报错undefined和unll没有这个toString属性
//radix 是以10进制为基底转换为别的进制
//用法:要转的数据.toString
var demo = 123;
var str = demo.toString();
console.log(str); //打印出"123"
引式内容转换
- isNaN()
- ++/-- +/-(一元正负)
- -,*,/,%
- &&,||,!
- <,>,<=,>=
- == !=
//isNaN()当你把数放在括号里面的时候他能判断这个是是不是NaN,然后给你返回回来
console.log(isNaN(NaN)) //打印true
console.log(isNaN(123))//打印false
console.log(isNaN("123"))//打印false
console.log(isNaN("adc"))//打印true
console.log(isNaN(null)//打印false
console.log(isNaN(undefined)//打印true
//isNaN在内部 执行了一个Numbar方法
//比如isNaN("abc")
//首先执行了Numbar("abc")看是不是NaN 如果是就返回NaN
//所以这个numbar它没有显示的去调用,是隐式的去调用
//++,先调用numbar
var a = "123";
a++;//打印出a-->124;
var a = "abc";
a++;//打印出a-->NaN;
//+/- 先调用numbar
var a = + "abc";
console.log(typeof(a))//打印出numbar
//+ 隐式类型转换调用的是string
var a = "1" + 1;
console.log(typeof(a))//打印出string
//-,*,/,%隐式类型转换调用的是numbar
// <,>,<=,>=如果有数字就调用numbar
var a = "1" < 2;
console.log(typeof(a))//打印出boolean
// == ,!=
var a = "1" == 1;
console.log(typeof(a))//打印出boolean true
//特殊的
undefined>0 //打印false
undefined<0 //打印false
undefined==0 //打印false
null>0 //打印false
null<0 //打印false
null==0 //打印false
undefined == null //打印true
NaN == NaN //不等于任何东西
不发生类型转换
=== !==(绝对的等于 绝对不等于)
1 !== "1" //true1
!== 1 //false
NaN === NaN //false
还有一种特殊的
//a在没有定义的情况下
typeof(a); //用console.log打印出undefined
//typeof返回的值类型 都是string类型
typeof(typeof(a)) //用console.log打印出string
JS函数、初始作用域
【函数】
- 返回值
//函数声明
//第一种
function box(参数){
//内容
};
//第二种叫命名函数表达式
var box = function test(参数){
//内容
}
box.name-->test
//第二种写法function后面就成了表达式,有没有名字无所谓,所以延伸出了第三种写法
//第三种叫匿名函数表达式-->函数表达式
var box = function (参数){
//内容
}
box.name -->box //函数执行box();
- 参数
形式参数-->形参a,b
function test(a,b){
var c= a+b
console.log(c);//等于3
}
//实际参数--实参1,2
test(1,2);
不定参数
- 例子一
function test(a){
//a 就等于1
//2,3不用管
//隐式的方法arguments[1,2,3]实参列表
//找到多余的实际参数
}
test(1,2,3);//不定参数
- 例子二
function test(a,b,c,d){ //a 等于1
//b 等于2
//c 等于3
//d 等于undefined
//找到形参的长度sum.length
}
test(1,2,3);
不定参的好处
var resultfunction sum(){
for(var i = 0; i<arguments.length){
resuit += arguments[i];
i++;
}
console.log(result);
}
sum(1,2,3,4,5,6,7,8,9);
function sum(a,b){
a = 2;
console.log(arguments[0]); //打印出2,参数a改变了
}
sum(1,2);
function sum(a,b){
a = 2;
arguments[0] = 3
console.log(arguments[0]); //打印出3,参数a改变了,可以相互改变
}
sum(1,2); //JS的映射规则
function sum(a,b){
b = 2;
arguments[1];
console.log(arguments[0]);
}
sum(1); //JS的映射规则
打印出undefined,实参列表传进来的时候他就有几个,就算我让b等于2,它也不会往arguments里面加了,因为他根本就没有,这个时候b就当一个变量用了,他跟实参不映射,因为形参比实参多了一位b,只有他们相等的时候他们才会有映射的规则,不相等的时候, 形参多了他不对应实参了,他们之间不映射。
- 结束条件加返回值return
- 结束函数,如果没有写,系统默认自带return
- 把值返回到函数外部
function sum(){
return 123
}
var box = sum();-->返回123
作用域初探
作用域定义:
- 变量(变量作用于又称上下文)和函数生效(能被访问)的区域
全局、局部变量
- 作用域的访问顺序
- 函数里面可以访问函数外面的东西在script标签上定义的变量叫
全局变量
- 在函数内部定义的变量叫
局部变量
//a是全局变量
var a = 123;function test(){
console.log(a);//打印出来是123
//b是局部变量
var b = 123;
function demo(){
var c = 234;
console.log(a);
console.log(b);
}
console.log(c);//报错c is not defined;
};
test();
console.log(b);//报错b is not defined
function test(){
var a = 123;
}
function demo(){
var b = 12;
}//不能相互访问
JS递归
写一个函数实现n的阶乘
// n! = n*(n-1)!;
//找规律
//找出口
function mul (n){
//n的阶乘
for(var i = 1; i <= n;i ++){
num *= i;
}
if(n == 1){
return 1;
}
return n*mul(n-1);
}
//递归mul(5);
mul(5) ==> 5*mul(4);
mul(4) ==> 4*mul(3);
mul(3) ==> 3*mul(2);
mul(2) ==> 2*mul(1);
//例子:写一个斐波那契数列
//fb(n) = fb(n-1)+fb(n-2)
function fb(n){
if( n == 1 ||n ==2 ){
return 1;
}
return fb(n-1) + fb(n-2);
}
fb(5) ==> fb(4) + fb(3)
fb(4) ==> fb(3) + fb(2)
fb(3) ==> fb(2) + ..
预编译执行过程
JS运行三部曲
- 第一步:语法分析
- 第二部:预编译
- 第三部:解释执行
预编译
语法分析也叫语义分析,语法分析他是通篇执行的一个过程,比如我写了好多行代码,这些代码在执行的时候他是解释一行执行一行,但是在执行之前系统执行的第一步它会扫描一遍,看看有没有低级的语法错误,比如少些个括号,带个中文之类的,它会通篇扫描一遍,但是不执行,这个通篇扫描的过程叫语法分析,通篇扫描之后它会预编译,然后在解释一行执行一行,也就是解释执行
预编译前奏
- imply global 暗示全局变量: 即任何变量,如果变量未经声明就赋值,自定义变量就为全局对象所有
a = 123;
var a = b = 123;
- 一切声明的全局变量,全是window的属性
var a = 123; //===> window.a = 123;
//例子:
function test (){
console.log("a");
}
test(); //成功打印出a,
box(); //写在方法之前也成功打印出a,为什么能执行就是有预编译的过程
function box (){
console.log("a");
}
var a =123;
console.log(a); //输出123
//--------------------------
console.log(a); //输出undefined,不报错;
var a = 123;
//--------------------------
//但是如果直接打印会报错;
console.log(b); //报错
//也是预编译的效果
//如果想偷懒记住两句话
//函数声明 整体提升
//变量声明 提升
解释一下函数声明整体提升:
如果你写一个函数声明,不管你写到哪里,系统总会把这个函数提到逻辑的最前面,所以你不管在哪里调用,在上面调用也好,下面调用也罢,本质上他都是在函数的下面调用,他会把函数声明永远给你提升到逻辑的
最前面
变量声明提升
var a = 123;
//实际上他是执行了两步
var a; //先声明变量
a = 123; //再变量赋值
所以系统提升的
变量
而不是变量带着值一起提升,所以在例子中a是打印出undefined;
注意,这两句话不是万能的 比如
function a(a){
var a = 123;
var a = function(){
}
a();
}
var a = 123;
这个就不是那两句话可以解决的
暗示全局变量: 即任何变量,如果变量未经声明就赋值,自变量就位全局对象所有
a = 10;
console.log(a);//打印10然后在window属性上有了a
window.a//10
var b = 20;//你声明了window也有b
window就是全局的域
预编译正式
- 创建AO对象
找形参和变量声明,将变量和形参名作为AO属性名,值为undefined - 将实参值和形参统一
在函数体里面找函数声明,值赋予函数体
function fn (a){
console.log(a);
var a = 123;
console.log(a);
function a (){};
console.log(a);
var b = function (){
}
console.log(b);
}
fn(1);
这个例子,参数,变量,函数名字都叫a,首先可以确定的是肯定会发生一个
覆盖
的现象,这样子就很矛盾,前面说了函数的预编译执行在函数执行的前一刻,可以这样子说,预编译就把这些矛盾给调和了。
- 首先预编译的
第一步 : 创建了一个AO对象,全称是Activation object 也就是作用域,也叫执行期上下文
AO{
}
第二步 : 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
AO{
a : undefined
b : undefined
}
第三步 : 将实参值和形参统一
AO{
a : 1;
b : undefined
}
第四步 : 在函数体里面找函数声明,值赋予函数体
AO{
a : 1,
b : undefined,
//b是是函数表达式,不是函数声明,所以不变
//然后有a了 有b了,然后将这个函数声明的名作为AO对象挂起来
d :
}
//然后值赋予函数体,也就是把a和b的属性值,变成函数体
//覆盖掉a 和b的的属性值
//也就变成下面的
//因为第四步的优先级最高
AO{
a : function a () {}
b : undefined,//b是是函数表达式,不是函数声明,所以不变
d : function d () {}
}
至此预编译过程结束,开始执行代码,执行函数
然后我们在看上面的例子
//预编译结果
AO{
a : function a () {}
b : undefined,
d : function d () {}
}
//开始执行代码
function fn (a){
//第一步开始打印a
//根据上面预编译的结果,
//所以打印结果是
function a () {}
console.log(a);
//第二步执行
var a = 123;
//因为在预编译的第二步里面,变量已经提升了
//所以第二步只执行的赋值
a = 123; //去AO对象里面去找a
//也就变成
AO{
a : 123 这个才是a的存储值
b : undefined,
d : function d () {}
}
var a = 123; //所以打印出123
console.log(a); //因为这句在话在预编译的时候系统已经看了
//所以不在看这句话
function a (){}; //所以下面的console.log(a)
//还是打印123;
console.log(a); //一样下面的var b这句话在预编译的时候已经看了,所以不在看
AO{
a : 123
//所以b的值变成function(){}
b : function(){}
d : function d () {}
}
var b = function (){
}
//所以打印出function(){}
console.log(b);
}
fn(1);
我们再看个例子
function test(a , b){
console.log(a);
c = 0;
var c;
a = 3;
b = 2;
console.log(b);
function b () {}
console.log(b);
}
//这下我们就很快的得出打印的东西
//a-->1
//b-->2
//b-->2
预编译不只会在函数体里面,也会发生在全局里面
全局里面的第一步是先生成GO Global Object,其他一样
GO === window
那么问题来了是GO先还是AO先
答案是先执行GO
作用域、作用域链精解
作用域精解
[[scope]] : 每个JavaScript函数都是一个对象。
对象中有些属性我们可以访问,但有些不可以,这些属性仅提供JavaScript引擎存取,[[scope]]就是其中一个。[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。
作用域链
- [[scope]]中存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
- 运行期上下文:
当函数执行时,会创建一个称为执行期上下文的内部对象。
一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行期上下文,当函数执行完毕,它所产生的执行上下文被销毁 - 查找变量: 从作用域链的顶端依次向下查找
function a() {
function b(){
var b = 234;
}
var a = 123;
b();
}
var glob = 100;
a();
首先我们来看上面的函数,这个是个整体的函数,a在这个全局的作用域里面,然后下面有glob这个变量,然后有这个a执行;
三种函数表达式
表达式(expression)
JavaScript中的一个短语,javascript会将其计算(evaluate)出一个结果。
程序中的常量是一个最简单的表达式。
变量名也是一种简单的表达式,它的值就是赋值给变量的值。
复杂表达式是由简单表达式组成。
- 函数名称是函数声明语句必须得部分。
它的用途就像变量的名字,新定义的函数对象会赋值给这个变量。 对函数定义表达式来说,这个名字是可选的:如果存在,改名字只存在函数体中,并指代该函数对象本身。
**注意:
==
以表达式来定义函数只适用于它作为一个大的表达式的一部分,比如在赋值和调用过程中定义函数。
- 1. 声明式函数
function area(width,height) {
return width*height;
}
var size = area(3,4);
解释器在执行每段脚本前会先搜寻变量和声明式函数。这表明函数可以在声明之前的位置被调用。
- 2. 表达式函数
var area = function(width,height) {
return width*height;
};
var size = area(3,4);
在解释器发现这条语句之前不能执行该函数。
- 3. 匿名函数
var area = (function() {
var width = 3;
var height = 5;
return width*height;
}());
立即调用该函数,一般只运行一次。
- 以函数表达式的方式来定义函数,函数的名称是可选的。
- 如果定义了函数名称,这时函数名称会变成函数内部的一个局部变量(非常适合用于递归)。
- 函数定义表达式特别适合用来定义只会用到一次的函数。