JavaScript 是属于网络的脚本语言!JavaScript被数百万计的网页用来改进设计、验证表单、检测浏览器、创建cookies,以及更多的应用。
编译性语言:
程序在执行之前需要一个专门的编译过程,把程序编译成为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高,依赖编译器,跨平台性差些。如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 |
为符合Web标准(w3c标准中的一项)结构、样式、行为相分离,通常会采用
外部引用
结构 | 行为 | 样式 |
---|---|---|
html | js | css |
相分离: 三个文件分开写,开发标准
变量(variable)声明、赋值分解
//单一var变量声明
var 变量名
命名规则
声明变量
//声明一个变量var a;
//声明多个变量var a = 100,
b = 200,
c = "holle world";
基本语法
//原始值几种类型
//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代码块
<script type="text/javascript">
console.log(a);
<script>
<script type="text/javascript">
var b = 1;console.log(b);
<script>
第一部分代码块会直接报错,但是不影响第二部分的打印b;
运算操作符
任何数据类型加字符串都等于字符串
+、 “-”、“”、“/”、“=”、“()” “++”、“--”、“+=”、“-=”、“/=”、“=”、“%=”;
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
//*=、/=相似 不做介绍
比较运算符
“>”、“<”、“==”、“>=”、“<=”、“!=”
逻辑运算符
“&&”、“||”、“!”
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;
短路
语句;条件语句
Switch语句
循环语句
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,终止本次循环,进行下次循环。
数组
数组对象
用来在单独的变量名中存储一系列的值。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]);
对象
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("里面放数据")
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转换成数
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()当你把数放在括号里面的时候他能判断这个是是不是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
【函数】
//函数声明
//第一种
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
打印出undefined,实参列表传进来的时候他就有几个,就算我让b等于2,它也不会往arguments里面加了,因为他根本就没有,这个时候b就当一个变量用了,他跟实参不映射,因为形参比实参多了一位b,只有他们相等的时候他们才会有映射的规则,不相等的时候, 形参多了他不对应实参了,他们之间不映射。
function sum(){
return 123
}
var box = sum();-->返回123
作用域定义:
全局、局部变量
全局变量
局部变量
//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;
}//不能相互访问
// 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运行三部曲
语法分析也叫语义分析,语法分析他是通篇执行的一个过程,比如我写了好多行代码,这些代码在执行的时候他是解释一行执行一行,但是在执行之前系统执行的第一步它会扫描一遍,看看有没有低级的语法错误,比如少些个括号,带个中文之类的,它会通篇扫描一遍,但是不执行,这个通篇扫描的过程叫语法分析,通篇扫描之后它会预编译,然后在解释一行执行一行,也就是解释执行
a = 123;
var a = b = 123;
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就是全局的域
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]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。
作用域链
function a() {
function b(){
var b = 234;
}
var a = 123;
b();
}
var glob = 100;
a();
首先我们来看上面的函数,这个是个整体的函数,a在这个全局的作用域里面,然后下面有glob这个变量,然后有这个a执行;
JavaScript中的一个短语,javascript会将其计算(evaluate)出一个结果。
程序中的常量是一个最简单的表达式。
变量名也是一种简单的表达式,它的值就是赋值给变量的值。
复杂表达式是由简单表达式组成。
**注意:
==
以表达式来定义函数只适用于它作为一个大的表达式的一部分,比如在赋值和调用过程中定义函数。
function area(width,height) {
return width*height;
}
var size = area(3,4);
解释器在执行每段脚本前会先搜寻变量和声明式函数。这表明函数可以在声明之前的位置被调用。
var area = function(width,height) {
return width*height;
};
var size = area(3,4);
在解释器发现这条语句之前不能执行该函数。
var area = (function() {
var width = 3;
var height = 5;
return width*height;
}());
立即调用该函数,一般只运行一次。