大标题

小节

一、js 基础

1. javascript的组成;

2. 运行js;

3. 打印信息;

4. 关键字var;

5. js中的数据类型;

6. NaN(not a number);

7. js运算符;

8. 数据类型转换;

9. 解决精度缺失问题;

10. 进制的转换;

11. undefined 和 not a defined

二、程序的结构-单双分支

2.1 分支结构1. 单分支;2. 双分支;3. 多分支;

2.2 循环结构

1. 循环三要素;2. 循环语句;3. do-while和while的区别;4. 死循环;5. 循环中的关键字

三、函数

1. 函数的使用;

2. 函数的分类;

3. 函数的参数;

4. arguments;

5. return;

6. 作用域;

7. 变量提升;

8. 递归


一、js 基础

网页的组成:
(1)结构:html
(2)样式:css
(3)行为:JavaScript

1. JavaScript(简称“js”)的组成:

(1)DOM: 文档对象模型,操作 html(在 js 中叫 DOM),网页结构;

(2)BOM: 浏览器对象模型,js 要运行在浏览器中,意味着 js 不仅要遵循自己的规则,还要遵循浏览器的规则(即BOM),提供了操作浏览器的方法;

(3)ECMAScript: 语法规范,规定了 js 怎么写,写什么;



2. 运行 js

js 也是一种脚本语言,有两种方式在HTML页面进行引入。
(1)外联 js: <script src="相对路径"></script>(2)内部 js: <script type="text/javascript">//...</script>



3. 打印信息

跟编程没有任何关系,用来观察数据处理的结果,用来测试,用来调bug。
(1)向 页面 打印信息:document.write();       操作的是 DOM,影响页面(坑: 页面正在加载时执行 docment.write(),页面正常;页面加载完执行docment.write(),页面被覆盖);

(2)向 控制台 打印信息:console.log();       操作的是 浏览器,既不影响页面,也不影响操作;

(3)向 浏览器弹出框 打印信息:alert();      操作的是 BOM,需要手动关闭;



4. 关键字 var

(1)原理: 我们所写的程序运行在内存中,当我们使用关键字 var 声明一个变量时,计算机会从内存中分一个空间,为我们存放 不同类型 的内容做准备。

(2)变量命名规则:

  • 只能以 字母_$ 开头;
  • 尽量使用数据类型做前缀;
  • 不允许使用 关键字保留字


5. js 中的数据类型

(1)字符型(String): 所有用引号引起来的数据 “” 和 ‘’ ;
(2)数值型(Number): 0123456789;
(3)布尔型(Boolean): true 和 false;
(4)undefined: undefined;
(5)对象型(object): {} 就是对象;
(6)数值(arr): [] 就是数值

  • 检测数据类型的方法:
    (1)typeof :用来检测数据类型;
    typeof 返回七种可能的值:“number”、“string”、“boolean”、“object”、“symbol”、“function”和“undefined”。
console.log("基础数据类型");
 console.log(typeof ""); //string
 console.log(typeof 1);  //number
 console.log(typeof true);  //boolean
 console.log(typeof undefined);  //undefined
 console.log(typeof null);  //object
 console.log(typeof []);  //object
 console.log(typeof {});  //object
 
 console.log("引用数据类型");
 console.log(typeof function(){});  //function
 console.log(typeof new Function()); // function
 console.log(typeof new Date()); //object 
 console.log(typeof new RegExp()); //object
  • ① 对于基本类型,除 null 以外,均可以返回正确的结果。
  • ② 对于 null ,返回 object 类型。
  • ③ 对于引用类型,除 function 以外,一律返回 object 类型。
  • ④ 对于 function 返回 function 类型。

其中,null 有属于自己的数据类型 Null , 引用类型中的 数组、日期、正则 也都有属于自己的具体类型,而 typeof 对于这些类型的处理,只返回了处于其原型链最顶端的 Object 类型,没有错,但不是我们想要的结果。

(2)instanceof具体参考

instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 在这里需要特别注意的是:instanceof 检测的是原型。

(3)constructor具体参考

(这里依然抛开null和undefined)乍一看,constructor似乎完全可以应对基本数据类型和引用数据类型,都能检测出数据类型,事实上并不是如此:声明了一个构造函数,并且把他的原型指向了Array的原型,所以这种情况下,constructor也显得力不从心了

(4)Object.prototype.toString.call()

jQuery源码就是采用这种方法检测的。

var a = Object.prototype.toString;
console.log(a.call("aaa"));   //[object String]
console.log(a.call(1));       //[object Number]
console.log(a.call(true));    //[object Boolean]
console.log(a.call(null));    //[object Null]
console.log(a.call(undefined));   //[object Undefined]
console.log(a.call([]));      //[object Array]
console.log(a.call(function() {}));   //[object Function]
console.log(a.call({}));     //[object Object]
console.log(a.call(new Date()));   //[object Date]
console.log(a.call(new RegExp()));   //[object RegExp]


6. NaN(not a number)

(1) 不是一个数字的数值型数据,非法的运算 会得到 NaN;
(2)NaN 不等于 NaN;
(3)isNaN()是不是一个数字;检测 NaN;
得到 NaN 时,返回 true,如果是数字,返回 false。

console.log(isNaN(NaN));//true
console.log(isNaN(123));//false



7. js 运算符:

(1)算术运算符:

  • -、*、/、%,会把两边的数据,当成数值进行运算;
  • + 号两边只要出现一个字符,那么就不是加法了,而是字符串的拼接;

(2)关系运算符:

  • 只要关系运算符两边,存在数值,会把两边的数据,当成数值进行运算;
  • 两边都是字符,会按照字符的比较规则,比较(逐位比较);
  • === 除了比较值,还比较了类型,不存在类型转换;
console.log(1 == true);    //true
console.log(1 ===  true); //false

(3)逻辑运算符:||&&!

(4)赋值运算符 = += -+ *= /= %=

(5)自增 ++ 和自减 -- 每次变化1:

  • ++a 先运算,再执行
  • a++ 先执行,再运算


8. 数据类型转换

(1)隐式类型转换:
字符转数值-*/%

数值转字符+

其他转 boolean:if(){} 括号中的隐式转换

  • 非 0 为 true,0 为 false;
  • 所有非空字符(即"",而不是" ")为 true,空字符为 false;
  • undefined 为 false;
  • 所有对象和数组(包括{}[],除了 null)都为 true;
  • NaN 为 false;
  • null 为 false;

其他

  • true 会转成1,false 会转成0;
  • NaN 与任何值(除了字符)运算得到 NaN;
console.log(1 + "1");//11
console.log(2 + false);//2   false -> 0
console.log(1 + true);//2   true-> 1
console.log(true + false);//1
console.log(undefined + 1);//NaN
console.log(undefined + NaN);//NaN
console.log(undefined + null);//NaN
console.log(null + 1);//1   null -> 空

(2)显式(强制)类型转换
字符转数值Number()parseInt()取整、parseFloat()取浮点数、Math.round()四舍五入取整

console.log(parseInt("123abc456"));//123
console.log(parseInt("abc456"));//NaN
console.log(parseInt("12.34"));//12
console.log(parseInt("12.84"));//12

console.log(parseFloat("123.1415avc"));//123.1415
console.log(parseFloat("123.14avc15"));//123.14
console.log(parseFloat("123.am1415"));//123
console.log(parseFloat("123am.1415"));//123
console.log(parseFloat("12am3.1415"));//12
console.log(parseFloat("am123.1415"));//NaN

console.log(Number("123a"));//NaN
console.log(Number("123.12"));//123.12

console.log(Math.round(123.4));//123
  • parseInt() 和 parseFloat() 转换规则:
    从左到右依次检测字符,遇到数字就转,遇到字母就停,第一位是字母就是 NaN;
  • Number():
    严格转换,小数点也能转换,只要出现一个非数字的字符,无论在哪个位置,都是 NaN。

数值转字符+""toString()转成字符、toFixed(n)四舍五入且保留n(n>=0)位小数

var num = 10;
var str = num + "";
var str = num.toString();
var str = num.toFixed();



9. 解决精度缺失问题
var n = 0.1 + 0.7;
n = parseFloat(n.toFixed(1))



10. 进制的转换

(1)十进制转其他: toString()

var num = 110;
console.log(num.toString(2));//十进制转二进制 1101110
console.log(num.toString(8));//十进制转八进制 156
console.log(num.toString(16));//十进制转十六进制 6e

(2)其他转十进制: parseInt()

var str = "110";
console.log(parseInt(str,2));//二进制的转十进制 6
console.log(parseInt(str,8));//八进制转十进制 72
console.log(parseInt(str,16));//十六进制转十进制 272

(3)其他转其他
先转成十进制,再用 toString() 进行转换;



11. undefinednot a defined

(1)undefined: 定义了没赋值;
(2)not a define: 未定义、一种报错;




二、程序的结构-单双分支

逻辑的不同,程序分为三种结构:顺序结构、分支结构、循环结构

2.1 分支结构
1. 单分支

if(判断条件){},只要“判断条件”为真,就会执行花括号中的操作。

  • 判断条件为真,可以是一个 boolean值为 true 的变量,非 0 的数字,具体可以参考 第一点里面的第8条-数据类型转换-其他转 boolean。


2. 双分支
  • if(){}else{}
  • 三目表达式:条件 ? 条件为真时的操作 : 条件为假时的操作;


3. 多分支

(1)if()

if(){
}
else if{
}
else{
}

(2)switch(){}

//switch语句名
//()要判断的值
//{}要执行的内容
//default:不是必须的,常只有一个,放在最后,不需要加break;
switch(变量){
  case 变量值0:
    操作;
	break; 
  case 变量值1:
	操作;
	break;
  default:
	当 case 条件不满足是执行的操作(默认操作);
}
  • case 穿透: 当某一条 case 判断成功时,执行了当前 case 后的语句,后面所有的 case 都不再验证判断条件,直接执行后面所有的语句;
var n = 2;
switch (n){
  case 1:
    console.log("星期一");
  case 2:
    console.log("星期二");
  case 3:
    console.log("星期三");
  case 4:
    console.log("星期四");      
}

//打印结果
星期二
星期三
星期四
  • 阻止 case 穿透: break 在 switch 中专门用来阻止 case 穿透。


2.2 循环结构
1. 循环三要素:

(1)声明计数器,比如:var i
(2)计数器的改变;
(3)停止条件;



2. 循环语句

(1)while(){}

//while语句名
//()执行条件、停止条件
//{}执行语句、循环体
var i = 0;
while(i<10){
	console.log(i);//0 1 2 3 4 5 6 7 8 9
	i++;//计数器常放最后
	console.log(i);//1 2 3 4 5 6 7 8 9 10
}
//当 i<10 时,执行循环体,否则不执行。执行次数 10
  • 求100-999之间的水仙花数:
//水仙花数: a^3 + b^3 + c^3 = abc;
var i = 100;
while(i<1000){
  var a = i%10; //取个位数
  var b = parseInt(i%100/10); //取十位数
  var c = parseInt(i/100); //取百位数
  if(i == a*a*a + b*b*b + c*c*c){
    console.log(i)
  }
  i++;
}

(2)do{}while(){}
特性:不管条件是真还是假,do 和 while 都会进行循环;

//do 语句名 {}do的执行语句、循环体
//while 语句名 ()停止条件 {}while的执行语句
//循环条件若放在 while 中,将会造成死循环

(3)for(){}

//for 语句名 ()条件组 {}循环体
for(var i=0;i<10;i++){
	console.log(i);//0 1 2 3 4 5 6 7 8 9 
}



3. do-while 和 while的区别

do-while不管任何情况下都至少会执行一次。

var i = 0;
do{
	console.log("do执行:"+ i); //0 1 2 3 4 5 6 7 8 9
	i++;
} while ( i < 10){
	console.log("while执行:"+ i); //10



4. 死循环:

无法替自身控制结束的循环,称为死循环

死循环的应用:每次判断一个条件,直到满足条件,利用 break 跳槽循环(常使用 while())。

  • 篮球从5米高的地方掉下来,每次弹起的高度时原来的30%,经过几次弹起,篮球的高度是0.1?
var i = 0;
var h = 5;
while(true){
  h = h * 0.3;
  i++;
  if(h<0.1){
    break;
  }
}
console.log(i); //4


5. 循环中的关键字

(1)break,跳出当前循环、结束当前循环,不再执行剩余代码。
(2)continue,跳过本次循环,用于停止当次循环,回到循环起始处,进入下一次循环操作。。

var i = 0;
while(i<10){
	console.log(i);
	if(i==5){
		break;
	}
	i++;
}
// 打印结果:0 1 2 3 4 5

var i = 0;
while(i<10){
	console.log(i);
	if(i==5){
		continue;
	}
	i++;
}
//造成死循环。当i=5时,跳过本次循环,即不执行i++,再次循环时i还是=5

var i = 0;
while(i<10){
	i++;
	if(i==5){
		continue;
	}
	console.log(i);
}
//1 2 3 4 6 7 8 9 10
//当 i=5 时,跳过本次循环及后面的逻辑,再返回到花括号中第一句代码重新开始循环判断




三、函数

概念: 函数是由事件驱动的,或者当它被调用时可执行的可重复使用的代码块;

1. 函数的使用

(1)创建函数

  • 声明式 创建函数: function 函数名(){//...}
  • 赋值式(也叫字面量式) 创建函数: var 函数名 = function(){//...}

(2)函数调用方式:

  • 自动执行: 函数名();
  • 事件执行: obtn.onclick = 函数名;

(3)函数执行时机:

  • 任何情况下,只要 函数名(),立即执行;
  • 当需要用事件触发函数时,不能加小括号,加了就会立即执行;
function bar () {
  console.log(1);
}
console.log(bar);
//打印结果:ƒ bar () {
//  console.log(1);
//}

console.log(bar()); //打印结果:1

(4)使用/执行: 函数名();

<button>按钮</button>
var obtn = document.querySelector("button");
obtn.onclick = say;

function say(){
	console.log("hello");
}

(5)函数的好处:
① 重复使用;
② 选择执行;
③ 忽略细节(就想空调一样,不会组装,但是会使用);



2. 函数分类

(1)有名函数: function 函数名(){};
(2)无名函数: funtion(){};
(3)匿名函数: (function(){})()

  • 区别:
    ① 无名函数: 不能直接写,必须通过事件调用,或者作为参数、返回值等使用;
    ② 匿名函数: 会立即执行,自动执行(即:不用主动调用,自己执行);


3. 函数的参数

形参: 形式的参数,函数定义时,小括号内的参数;
实参: 实际的参数,函数执行时,小括号内的参数;

function fn(n){ //n 形参
	console.log(n);
}
fn(12); // 12实参

(1)实参数量 = 形参数量,参数一一对应;
(2)实参数量 > 形参数量,多出来的实参到 argument 中;
(3)实参数量 < 形参数量,多出来的形参是 undefined



4. arguments

(1)伪数组:

  • 理解:伪数组像数组一样有 length 属性,也有 0、1、2、3 等属性的对象,看起来就像数组一样,但不是数组。
  • 特点:
    ① 具有 length 属性;
    ② 按索引方式存储数据;
    ③ 不具有数组的 push()、pop() 等方法;
  • 伪数组转换为真数组
    ① 方法一:Array.prototype.slice.call(伪数组);
    ② 方法二:Array.prototype.slice.apply(伪数组);
    ③ 方法三:Array.prototype.slice.bind(伪数组);
    ④ 方法四:Array.from(伪数组),返回的就是真数组;

(2)arguments

function fn(a,b){
	console.log(a); //4
	console.log(b);  //6
	console.log(arguments);//Arguments(4) [4,6,8,3]
}
fn(4,6,8,3)

JavaScript 结构体 是否有成员 js 结构体定义_i++

  • 示范将伪数组变成真数组:
function fn(a,b){
	console.log(arguments);//Arguments(4) [4,6,8,3]
	var arr = Array.prototype.slice.call(arguments);
	console.log(arr);  //[4,6,8,3]
}
fn(4,6,8,3)

(3)arguments.callee 保存了自身所在的函数,“递归”中使用(但是后期递归舍弃了该方法);

function fn(a,b){
	console.log(arguments.callee);//[4,6,8,3]
}
fn(4,6,8,3)

JavaScript 结构体 是否有成员 js 结构体定义_i++_02

(4)运用场景:无法确定实参的个数是多少时。

//求任意个数字的和
function fn(){
	var sum = 0;
	for(var i = 0; i<arguments.length; i++){
		sum += arguments[i];
	}
}
fn(4,6,8,3)



5. return

把函数里面的数据 返回到 函数外面,使我们可以在函数外面拿到函数里面的数据。

(1)作用:① 终止循环;② 返回值;

function fn(){
	return 123;
}
var a = fn();
console.log(a);//123
console.log(fn());//123
  1. 返回值: 返回值就是 将函数处理后的数据,返回到函数名上(利用这一点可以做一个简单的“闭包”,也可参考菜鸟教程), 便于其他程序或用户调用或做二次使用。
  2. 不一定所有函数都有返回值,只有需要返回数据的函数才加 retrun
  • 闭包具体在后面提及,这里写一个示例。
function a(){
   var m = 0;
   return function(){
     m++;
     console.log(m)
   };
 }
console.log(a());
//打印结果:返回的是return后面的函数,不包括 var m = 0;这一条语句。
//ƒ (){
//  m++;
//  console.log(m)
//}
a()();  //11  这样相当于执行函数a的整个部分,所以多次调用,会一直先让m=0,再去执行return语句
a()();  //11
//从上面的打印结果,我们可以看到打印a()的结果是一个函数。
function a(){
   var m = 0;
   return function(){
     m++;
     console.log(m)
   };
 }
var b = a();  //将这个函数结果定义一个新的变量保存起来
b();  //11
b();  //12
b();  //13

//上面这种保存函数的方法,执行结果就相当于
//function a(){
  var m = 0;
  function b(){ //变量m和函数b组成了一个闭包
    return m++;
  }
//}
console.log(b());  //因为函数b的返回值是return,要想查看结果,就要console.log(b());而不能直接b()
console.log(b());  //类似数组方法sort()的执行过程
console.log(b());

(2)return 的几个返回值:
return true;,返回正常的处理结果;终止处理。

return false;,返回错误的处理结果;终止处理;阻止提交表单;阻止执行默认的行为。
return false; 只在当前函数有效,不会影响其他外部函数的执行。

return;,把控制权返回给页面。

(3)breakcontinuereturn 的用法及区别:

关键字

break

continue

return

相同之处

三个都会将此时进行的语句停止。

不同之处

break 是立即结束语句,并跳出语句,进行下个语句执行。

continue 是停止当前语句,并从头执行该语句。

return 停止函数。

使用环境

breakcontinue 是用在循环或switch语句中,用来退出循环。

return 是用在函数语句中。return 语句应用范围只能出现在函数体内,出现在代码中的其他任何地方都会造成语法错误!

举例

for(i=0; i<5; i++){

  if(i == 3){

    break;

  }

  console.log(i);

}

for(i=0; i<5; i++){

  if(i == 3){

    continue;

  }

  console.log(i);

}

function format(d){

  if(d<10){

    return “0” + d;

  } else {

    return d;

}

返回值

0 1 2

0 1 2 4

结果分析

当 i=3 时,直接退出for这个循环。这个循环将不再被执行!直接进入下一个语句。

当 i=3 时,直接跳出本次for循环。继续执行这个循环语句的后面的循环条件。

语句结束函数执行,返回调用函数,而且把表达式的值作为函数的结果。


6. 作用域

作用域分为:全局作用域、局部作用域、块级作用域,这里主要讲前两种。

  1. 作用域:作用域是可访问变量的集合。 —— 参考自菜鸟教程
  2. 在 JavaScript 中, 对象和函数同样也是变量。
  3. 函数作用域:作用域在函数内修改。即变量在函数内部作用的范围(区域),有函数的地方就有作用域。

(1)全局作用域: 变量在函数外定义,即为全局变量。全局变量有全局作用域:网页中所有脚本和函数均可使用。

var carName = " Volvo";
 
// 此处可调用 carName 变量
function myFunction() {
    // 函数内可调用 carName 变量
}
  • 如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量,而全局变量一般存到 window 中,我们通常会省略window
    以下实例中 carName 在函数内,但是为全局变量。
myFunction();
// 此处可调用 carName 变量 
function myFunction() {
  carName = "Volvo";
  // 此处可调用 carName 变量
}
console.log(carName);  //Volvo
console.log(window.carName);  //Volvo

(2)局部作用域: 变量在函数内声明,变量为局部作用域。每一个函数都有一个局部的作用域,局部作用域只能在函数内部访问。;

// 此处不能调用 carName 变量
function myFunction() {
    var carName = "Volvo";
    // 函数内可调用 carName 变量
}

① 因为 局部变量只作用于函数内,所以在不同的函数内部可以使用相同名称的变量。

局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。
函数内改变的只是该函数内定义的局部变量,不影响函数外的同名全局变量的值,函数调用结束后,局部变量占据的内存存储空间被收回,而全局变量内存存储空间则被继续保留。

③ 当你在 全局 和 局部 同时用 var 命名同名变量时,那么同名局部变量会在其所在函数内屏蔽全局变量,而优先使用局部变量。

var a = "10";
function fn() { 
  var a ="20";
  console.log(a)
}
fn(); //20
console.log(a);   //10 这里的a为全局变量

(3)变量生命周期
① JavaScript 变量生命周期在它声明时初始化。
② 局部变量在函数执行完毕后销毁。
③ 全局变量的生命周期一直存在,在页面关闭后销毁。

  • 将全局变量 变成 局部变量的方法:
    将变量放到匿名函数中。
(function(){
	var a = 3; 
})()
  • A、在匿名函数中,全局变量可以被匿名函数中的函数访问、使用;
    B、没有真正声明全局变量时(比如变量在函数内没有用 var 声明就赋值),也不会污染整个全局变量空间、占用内存。


7. 变量提升

在 JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。
函数及变量的声明都将被提升到函数的最顶部。

(1)变量提升
所有用 var 声明的变量都存在提升,提升到作用一开始的位置,在写了 = 号的位置赋值(即提前声明,在原本赋值的地方赋值);

console.log(b);//b is not defined
console.log(a);//undefined
var a = 10;
console.log(a);//10
console.log(b);//b is not defined
  • 相当于:
var a;
console.log(b);//b is not defined
console.log(a);//undefined
a = 10;
console.log(a);//10
console.log(b);//b is not defined

(2)函数提升
① 声明式: 整个函数提升(即提前声明,提前赋值),整个代码块提升到它所在的作用域的最开始执行;

fn();//hello
function fn(){
	console.log("hello");
}
  • 相当于:
function fn(){
	console.log("hello");
}
fn();//hello

② 赋值式(字面量式): 提升变量(即提前声明,但是没有提前赋值);

fn();//fn is not a funtion
var fn = function(){
	console.log("world");
}
  • 相当于:
var fn;
fn();//fn is not a funtion
fn = function(){
	console.log("world");
}


这里有一道题,结合作用域以及变量提升的知识点,请你给出打印结果。

console.log(v1);
var v1 = 100;
function foo() {
    console.log(v1);
    var v1 = 200;
    console.log(v1);
}
foo();
console.log(v1);

//打印结果:
//undefined
//undefined
//200
//100
  • 解释一下,上面代码相当于下面这段代码的缩写:
var v1; //在全局上,我们可以看到 v1 已经被 var 声明定义了,所以要被提升。
console.log(v1);  //undefined
var v1 = 100;
function foo() {
	var v1; //在局部作用域中,v1 被 var 声明,所以提前到作用一开始的地方。
   console.log(v1);  //undefined
    var v1 = 200;
    console.log(v1);  //200
}
foo();
console.log(v1);  //在全局获取不到局部作用域中的变量,这里的 v1 是全局变量


8. 递归

(1)递归就是在自身内部调用自己;
(2)缺点: 耗性能,逻辑稍微不太好理解;

  • 递归计算阶乘
//3*2*1
function fn(n){
 if(n == 1){
	  return 1;
  } else {
	return n*fn(n-1);
  }
}
fn(3);
  • 斐波那契数列:1,1,2,3,5,8,13,21,34…
//写一个数字n,找到第 n 个斐波那契数列;
function fn(n){
if( n == 1 || n== 2){
  	return 1;
  } else {
  	return fn(n-1) + fn(n-2);
}
fn(3);



下一篇—— js笔记(二)对象和构造函数、数组、this