JS学习笔记(1)

1、JAVASript简介

1.1JavaScript构成

Java三要素:事件源,事件类型,事件处理

核心(ECMAScript)

文档对象模型(DOM)Document Object Model

浏览器对象模型(BOM)Browser Object Model

1.1.1 ECMAScirpt

  • 语法
  • 类型
  • 语句
  • 关键字
  • 保留字
  • 操作符
  • 对象

(1)版本

ECMAScript 3.1 成为 ECMA-262 第 5 版,并于 2009 年 12 月 3 日正式发布。第 5 版力求澄清第 3版中已知的歧义并增添了新的功能。新功能包括原生 JSON 对象(用于解析和序列化 JSON 数据)、继承的方法和高级属性定义,另外还包含一种严格模式,对 ECMAScript 引擎解释和执行代码进行了补充说明。

(2)ECMAScirpt兼容性

  • 支持 ECMA-262 描述的所有“类型、值、对象、属性、函数以及程序句法和语义”;
  • 支持 Unicode 字符标准。
  • 添加 ECMA-262 没有描述的“更多类型、值、对象、属性和函数”。
  • 支持 ECMA-262 没有定义的“程序和正则表达式语法”。

(3)Web 浏览器对 ECMAScript 的支持

五大主流浏览器–内核:

IE – trident

Firefox – gecko

safari – webkit

chrome – webkit/blink

oprera – presto

1.2JavaScript特点

js运行在客户端–浏览器

js解释器:负责js的执行;

v8引擎负责js的执行,把代码编译成机器码;

JS 定位成弱类型,脚本语言:在运行的时候,不需要编译;

1 . JavaScript语言的特点

(1) JavaScript是一种脚本编程语言

这里要解释一下什么是脚本语言,也许很多读者之前已经接触过脚本语言,其实脚本语言是一种简单的程序,它是由一些ASCII字符构成,可以直接用记事本等文本编辑器编写,事先也不用编译,只需要利用解释器就可以解释执行。

(2) JavaScript是面向对象的语言

JavaScript是一种面向对象的语言,那就是说,它本身也可以创建对象,以及调用对象的操作。因此,JavaScript的诸多功能可以来自于脚本环境中各种对象的调用。

(3) JavaScript的简单性

之所以说JavaScript是简单的,首先是因为它是一种基于Java的基本语句和控制流之上的简单而紧凑的设计,这对于更进一步学习Java是一个非常好的过渡,其次是因为它的所有变量都是弱类型,并且都没有像其它需要编译的高级语言那样使用严格的数据类型。

(4) JavaScrip的安全性

JavaScrip就像Java一样是一种非常安全的语言,它不允许访问本地的硬盘,并且不允许把数据存入到服务器上,还不允许对网络文档进行修改和删除,只允许通过浏览器实现信息浏览和动态交互,这样确保了对数据的安全化操作。

(5) JavaScript的动态性

之所以说JavaScript是动态的,是因为它可以直接对用户或客户的输入操作做出响应,而不必经过web服务器或web服务器程序。
JavaScript对用户的响应是采用事件驱动的方式进行的。简单地说,事件驱动是指在页面中执行了某种操作后产生相应的动作,例如,按下鼠标、选择菜单以及移动窗口等都可以被视为事件,当事件发生后,就会有相应的事件响应该事件
(6) JavaScript的跨平台性
JavaScript同Java一样是与操作环境无关的,它只依赖于浏览器,只要客户的计算机浏览器支持JavaScrip,它就可以被正确解释执行。从而实现一次编写,到处运行。

1.3 JavaScript 使用

1.行内

给标签设置对应的属性,属性值是要执行的JavaScript代码

<body>
		<!-- 标签的属性可以写js  a(href)  div(onclick) p(onclick) -->
		<!-- 少用  	<a href="javacsript:void(0)">连接标签</a> -->
		<div style="height: 1000px;" onclick="alert('弹出一个警告框')"></div>
		<a href="javacsript:void(0)">连接标签</a>
		
	</body>

2.内嵌

使用script标签,标签需要闭合,标签内容是要执行的JavaScript代码。

<body>
		<div>页面div元素</div>
		
		<!-- script标签内部可以放置js语句,整个script建议放在</body>之前 -->
		<script type="text/javascript">
			alert('页面一打开弹出一个警告框');
		</script>
	</body>

3.外联

使用script标签,标签需要闭合,设置属性src,src的值是js文件路径。

<body>
		<div>页面元素</div>
		
		<!-- script标签内的JS语句可能对页面元素进行操作,整个标签建议写在所有页面标签之后 -->
		<!-- script只要写上SRC属性,就无法运行内嵌代码了 -->
		<script src="00js外联.js" type="text/javascript" charset="UTF-8"></script>
	</body>

注:

① 嵌入和导入的数量不受限制;

② 使用script 标签引入外部js文件时(标明src属性,不管有没有赋值),标签内容部分再填写js语句是不能执行的;

③ js代码直接写在一个独立的文件里面,该文件就是js文件,后缀是.js

1.4 JavaScript 输出

1.window.alert()

弹出一个有确定按钮的信息框,多用于信息警告.

<script type="text/javascript">
			window.alert('页面一打开弹出一个警告框');
		</script>

2.document.write()

// 文档流未关闭的情况下使用document.write,自动在后面追加内容
			document.write("111111");
			document.write("222222");
			
			//文档流未关闭情况下使用document.write,会覆盖内容
// 			window.οnlοad=function(){
// 				document.write("窗口界面所有资源下载完毕,文档流关闭")
// 			}

3.操作 HTML 元素

如需从 JavaScript 访问某个 HTML 元素,您可以使用 document.querySelector() 方法。
使用 innerHTML 来获取或插入元素内容:

            // 往界面元素上放置内容  .innerHTML
			// document.querySelector,界面从上往下找,找到第一个符合选择器的元素即停止,只找一个
			document.querySelector("div").innerHTML="往界面元素内放置内容";
			
			// document.querySelectorAll,界面从上往下找,找到所有符合选择器的元素,用数组形式返回
			// 对界面元素进行操作,一定记住加索引
			document.querySelectorAll("div")[0].innerHTML="界面修改内容";

4.confirm()

var mycon=confirm("确定删除此数据吗?");
			alert(mycon);

点击“确定”按钮返回TRUE;点击“取消”返回 FALSE。

5.window.prompt()

var myinput=prompt("请用户输入账户名");
			 console.log(myinput);

6.写到控制台

<script type="text/javascript">
			 console.log("控制台打印信息");
			 console.warn("打印警告信息");
			 console.error("代码出错");
			 alert(myinput);
			 
			 // js对象,大括号括起来key=value,key与value用冒号隔开,多组key-value之间用逗号隔开
			 console.table({name:"abby",sex:"女"});
			 
			 // 清除打印
			 console.clear();
			 
			 var myinput=prompt("请用户输入账户名");
			 console.log(myinput);
			 
		</script>

1.5 JavaScript 调试

<script type="text/javascript">
			// 调试:
//             1.看控制台报错
// 			   2.console.log()
// 			   3.控制台source面板,调试debugger.调试完记住取消断点.
			document.querySelector("div").onclick=function(){
				// debugger
				this.style.backgroundColor="#ccc";
				// debugger
				this.innerHTML="已点击";
				// debugger
				alert("谢谢惠顾");
			}
		</script>

1.6 JS获取和修改页面元素

1. 获取页面元素

  • document.querySelector(‘选择器’):

    如果该选择器对应多个元素,则只返回第一个。

  • document.querySelectorAll(‘选择器’) :

​ 获取选择器对应的全部元素;
​ 返回值是类数组

2.改变元素样式

语法是:document.querySelector(‘选择器’).style.属性 = ‘值’;

如果属性有横线(-),如background-color、font-size、border-radius、font-weight,则把横线去掉,同时横线后面的第一个字母大写,
如:backgroundColor、fontSize、borderRadius、fontWeight;

隐藏元素:document.querySelector('选择器').style.display = 'none';
改变字体颜色:document.querySelector('选择器').style.color = '#FF0000';
改变背景颜色:document.querySelector('选择器').style.backgroundColor = '#000000';
字体加粗:document.querySelector('选择器').style.fontWeight = 'bolder'

1.7 JavaScript 加号运算符

加号运算符可用于连接字符串:

var name ='花花';
var sex  ='男';
var s    = name+'是一个'+sex+'生';
			
console.log(s);
document.write(s);

1.8 JavaScript 事件

  • 事件的概念

事件是指可以被JS监测到的网页行为;如:鼠标点击、鼠标移动、鼠标移入/离开、键盘按键、页面加载等;

  • JavaScript事件的三要素:事件源、事件、事件处理

    H5特定事件列表:

    属性描述
    offlinescript文档进入离线状态时触发。
    onabortscript事件中断时触发。
    onafterprintscript文档被打印后触发。
    onbeforeprintscript文档被打印前触发。
    oncanplayscript媒体停止缓冲,可以开始播放时触发。
    oncanplaythroughscript媒体可以播放到结束时触发,无需停止缓冲。
    onchangescript元素发生变化时触发。
    onclickscript鼠标点击触发。
    ondblclickscript双击鼠标时触发。
    ondragscript元素被拖动时触发。
    ondragendscript拖拽操作结束时触发。
    ondragoverscript元素被拖放到有效目标上时触发。
    ondragstartscript拖拽操作开始时触发。
    onendedscript媒体到达终点时触发。
    onerrorscript发生错误时触发。
    onforminputscript表单获得用户输入时触发。
    onhaschangescript文档变化时触发。
    oninputscript元素获得用户输入时触发。
    oninvalidscript元素失效时触发。
    onkeydownscript键盘按下时触发。
    onkeyupscript按键释放时触发。
    onloadscript载入文档时触发。
    onloadeddatascript载入媒体数据时触发。
    onloadedmetadatascript媒体元素的媒体数据载入时触发。
    onloadstartscript浏览器开始载入媒体数据时触发。
    onmessagescript消息被触发时触发。
    onmousedownscript鼠标按键被按下时触发。
    onmousemovescript鼠标指针移动时触发。
    onmouseoutscript鼠标指针移出元素时触发。
    onmouseoverscript鼠标指针移入元素时触发。
    onmouseupscript鼠标按键释放时触发。
    onmousewheelscript鼠标滚轮转动时触发。
    onoinescript文档上线时触发。
    ononlinescript文档上线时触发。
    onpagehidescript窗口隐藏时触发。
    onpausescript媒体数据暂停时触发。
    onplayscript媒体数据开始播放时触发。
    onplayingscript媒体数据播放时触发。
    onprogressscript浏览器获取媒体数据时触发。
    onratechangescript媒体数据的播放比率改变时触发。
    onredoscript文档执行 redo 操作时触发。
    onresizescript调整窗口尺寸时触发。
    onscrollscript元素的滚动条滚动时触发。
    onselectscript元素被选中时触发。
    onstalledscript获取媒体数据发生错误时触发。
    onstoragescript载入文档时触发。
    onsubmitscript表单提交时触发。
    onsuspendscript浏览器获取媒体数据,但获取整个媒体文件中止时触发。
    ontimeupdatescript媒体播放位置改变时触发。
    onundoscript文档执行 undo 操作时触发。
    onunloadscript用户离开文档时触发。
    onvolumechangescript媒体音量发生变化,包括设置为“静音”时触发。
    onwaitingscript媒体停止播放,等待恢复时触发。

2、JavaScript 基础语法

2.1语法规范

<script type="text/javascript">
			// js的运行过程:第一步语法检查
			
			//1. 大小写严格区分
			console.log(document.querySelector("div"));
			var a =123;
			console.log(a);
			// console.log(A);报错
			
			// 2.空格不能写在标识符中间
			console.log(document  .  querySelector("div")); 
			console.log(do cument.querySelector("div")); 报错
			
			// 3.注释符
			// 这是单行注释符
			
			/*
			*  这是多行注释符
			*/
		   
		    // 4.语句结束符  分号,养成习惯
		    
			// 5.js可以表示值的直接量
			// 数字 字符串 布尔类型 数组 null  函数 对象
		   
		    // 6.js标识符可以用数字,下划线,英文字母,$,
		    // 数字不能作为开头
		    var a1 = 123;
		    // var 1a = 123;  语法不通过

		   
		</script>

2.2 关键字和保留字

具有特定用途的关键字:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aqLilYUr-1592232189686)(C:\Users\Administrator\Desktop\培训\JS\assets\importantword.png)]

不能用作标识符的保留字:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uyn3mcco-1592232189690)(C:\Users\Administrator\Desktop\培训\JS\assets\es3-baoliuword.png)]

2.3 变量

<script type="text/javascript">
			"use strict"; /* 严格模式下,变量必须用var声明 */
			
// 			浏览器拿到js代码:
// 			1.语法检查
// 			2.变量提升和函数提升
			/* {
				a:undefined
			  } 
			*/
//          3.语句执行(赋值语句,打印语句,函数调用,流程控制)

			console.log(a); /* undefined */
			var a = 123; /* var a,a=123 */
			
			// 一个变量没有值,打印变量为undefined
			console.log(a);
			
			/* aa is undefined ,没有用var 声明的变量没有变量提升 */
			// console.log(aa); 
			aa=200;
			console.log(aa);
			
			
			function print(){
				// 在函数里用var声明的变量,在函数外访问不到(局部变量)
				var num1=100;
				// 在函数里没有用var声明的变量,函数外可以访问到(全局变量)
				num2=200;
				document.write("11111");
				// 在函数外直接写好的变量(不管有没有var),在函数里面直接访问得到(全局变量)
				console.log(a,aa);
			}
			print();
			console.log(num2);
			
		</script>

2.4 var声明

<script type="text/javascript">
			// 使用var 可以连着声明多个变量,多个变量用逗号隔开
			// 1.只有被var声明的变量才有变量提升
			console.log(b1, b2, b3, c1);
			var b1, b2, b3 = 100;
			var c1 = c2 = c3 = 200;
			console.log(b1, b2, b3, c1, c2, c3);

			// 2.用var声明的变量用delete操作符删不掉;没有用var的变量可以被删掉
			console.log(delete b1); /* false */
			console.log(delete c2); /* true */

			console.log(b1);


			// 3.严格模式下use strict ,只能用var声明后的变量才能使用,否则报错

			// 4.函数里用var声明的量是一个局部变量,没有用var声明的是一个全局变量
			function myfn() {
				var variabal1 = "abc";
				variabal2 = "xyz";
			}
			myfn();
			console.log(variabal2);
		</script>

3.JavaScript 数据类型

JavaScript的数据类型分类:

  1. 原始类型:数字、字符串、布尔值、null(空)、undefined(未定义);存储于栈内存。

  2. 引用类型(也称对象类型):原始类型之外的类型,如数组、对象、函数等;存储于堆内存。

<script type="text/javascript">
			// 1. 原始类型:数字、字符串、布尔值、null(空)、undefined(未定义);存储于栈内存。
			// 2. 引用类型(也称对象类型):原始类型之外的类型,如数组、对象、函数等;存储于堆内存。
			
			// [原始类型]
			// 数字number
			123
			1.23
			
			//字符串string
			"12px"
			'green'
			
			// 布尔值Boolean
			true 
			false
			
			// null类型
			null
			
			//undefined类型
			undefined
			
			// [引用类型]
			// 数组Array
			var a1=[1,2,3]
			var a2=[{name:"sish"},{name:"fak"},{name:"djag"}]
			
			// 对象Obeject
			var a3={
				name:"sish",
				gender:"female"
			}
			
			// 函数function 
			function fn(){
				//js语句块
			}
			
			
			var a=123;
			var b=123;
			console.log(a===b);//true值相等,类型也相等
			
			
			var A=[1,2,3];
			var B=[1,2,3];
			console.log(A===B);//false
			
			
		</script>

3.1 typeof 操作符

检测给定变量的数据类型。

对一个值使用 typeof 操作符可能返回下列某个字符串:

  • “undefined”——如果这个值未定义;
  • “boolean”——如果这个值是布尔值;
  • “string”——如果这个值是字符串;
  • “number”——如果这个值是数值;
  • “object”——如果这个值是对象或 null;
  • “function”——如果这个值是函数。
		<script type="text/javascript">·
// 			typeof:检查类型的操作符,操作结果可能为
//                 (操作结果的类型只能是字符串string类型)
// 			原始类型:"undefined","Number","String","Boolean","Object"
// 			引用类型:"Object"/"function"
			
			var test;
			console.log(typeof test);/* "undefined" */
			
			test=123;
			console.log(typeof test);/* "Number" */
			
			test="abc";
			console.log(typeof test);/* "String" */
			
			test=true;
			console.log(typeof test);/* "Boolean" */
			
			test=null;
			console.log(typeof test);/* "Object" */
			
			// 引用类型
			test=[1,2,3];
			console.log(typeof test);/* "Object" */
			
			test={name:"花花"};
			console.log(typeof test);/* "Object" */
			
			test=function(){
				console.log("abc");
			}
			console.log(typeof test);/* "function" */
			console.log(typeof console);/* "Object" */
			console.log(typeof console.log );/* "function" */
			
			
			console.log(typeof mytest);/* "undefined" */
		</script>

3.2 Undefined类型

在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined。

取变量(不管是取空间还是空间里的值) ,先看这个空间有没有,再看有没有值:声明了一个空间,但是没有值的空间,再取值就会取到undefined;取一个不存在的空间的数据时,程序则报错。

3.3 Null类型

null 值表示一个空对象指针,而这也正是使用 typeof 操作符检测 null 值时会返回"object"的原因。

null和undefined区别

区别:

•null

– 用来描述空值;

– typeof null:返回的是字符串object,也就是说可以把null看成一个特殊的对象;

– 通常来讲我们把null看成他自有类型的唯一成员;

•undefined

– undefined表明变量没有初始化;

– 如果函数没有返回值,则返回undefined;

– typeof undefined:返回的是字符串undefined;

– ==认为NULL和undefined是相等的;===则返回false;
	<script type="text/javascript">
// 			1.概念
// 			null表示空值
// 			undefined表示没有值
// 			
// 			2.出现
// 			null手动赋值
// 			undefined声明了变量不给赋值+函数被调用,但没有返回值
// 			
//          3.typeof操作符
//          null结果为"object",undefined结果为"undefined"
// 
// 			4.联系
// 			null==undefined(相等性判断),值为true
//          null===undefined (全等性判断),值为false

	       var variabal1=null;
	       function fn(){
			  console.log("函数被调用所以有一个打印语句");
		  }
		   console.log(fn());/* undefined */
		
	       console.log(null==undefined); /* true */
		   console.log(null===undefined);/* false */
	</script>

3.4 Boolean类型

该类型只有两个字面值: true 和 false。以下是为变量赋Boolean 类型值的例子:

var found = true;

var lost = false;

Boolean 类型的字面值 true 和 false 是区分大小写的。也就是说,True 和 False(以及其他的混合大小写形式)都不是 Boolean 值,只是标识符。

<script type="text/javascript">
			// boolean类型,true false
			// Boolean 操作规则:
			
			// Boolean	 true 			 false
			// number	任何非零数字值	   0和NaN 
			// string	任何非空字符串	  "" 空字符串 ''
			// object	所有的引用类型	      null
			// undefined				  undefined
			
			console.log(Boolean); //ƒ Boolean() { [native code] }

			var str = "0";
			console.log(Boolean(str));

			var num = NaN; //not a number
			console.log(Boolean(num)); //false

			var obj = function() {}; //所有的引用类型,{},[]
			console.log(Boolean(obj)); //true

			console.log(Boolean(null)); //false
		</script>

3.5 Number类型

这种类型用来表示整数和浮点数值(浮点数值在某些语言中也被称为双精度数值)。

1.整数数值

十进制整数可以像下面这样直接在代码中输入:

var intNum = 55; // 整数

整数还可以通过八进制(以 8 为基数)或十六进制(以 16 为基数)的字面值来表示。其中,八进制字面值的第一位必须是零(0) ,然后是八进制数字序列(0~7) 。如果字面值中的数值超出了范围,那么前导零将被忽略,后面的数值将被当作十进制数值解析。请看下面的例子:

var octalNum1 = 070; // 八进制的 56
var octalNum2 = 079; // 无效的八进制数值——解析为 79
var octalNum3 = 08; // 无效的八进制数值——解析为 8

八进制字面量在严格模式下是无效的,会导致支持的 JavaScript 引擎抛出错误。

十六进制字面值的前两位必须是 0x,后跟任何十六进制数字(0~9 及 A~F)。其中,字母 A~F可以大写,也可以小写。如下面的例子所示(严格模式下也可以使用):

var hexNum2 = 0x1f; // 十六进制的 31

在进行算术计算时,所有以八进制和十六进制表示的数值最终都将被转换成十进制数值。

2.浮点数值

所谓浮点数值,就是该数值中必须包含一个小数点,并且小数点后面必须至少有一位数字。

如果小数点后面没有跟任何数字,那么这个数值就可以作为整数值来保存。同样地,如果浮点数值本身表示的就是一个整数(如 1.0) ,那么该值也会被转换为整数。

对于那些极大或极小的数值,可以用 e 表示法(即科学计数法)表示的浮点数值表示。用 e 表示法表示的数值等于 e 前面的数值乘以 10 的指数次幂。

var floatNum = 3.125e7; // 等于 31250000

也可以使用 e 表示法表示极小的数值,如 0.00000000000000003,这个数值可以使用更简洁的 3e-17表示。

浮点数值的最高精度是 17 位小数,但在进行算术计算时其精确度远远不如整数。

var testNum1 = 0.1234567890123456789+0.1234567890123456789;
var testNum2 = 0.0000000000000000011+0.0000000000000000011;
//17位小数显示与科学计数法

var a = 0.1;
var b = 0.2;
if (a + b == 0.3){  
 alert("You got 0.3.");
}
//console.log(a+b);//这个误差告诉我们,无法测试特定的浮点数值,不要做这样的测试!

解决方法:

//1、转化成整数进行计算
var a = 0.1;
var b = 0.2;
if ((a*10 + b*10/10 == 0.3){ 
 alert("You got 0.3.");
}

//2、设定保留的小数位,实际开发过程中保留2~3位小数位足够了
(0.1 + 0.2).toFixed(3)

//3、转化成整数Math.floor(a)、Math.ceil(a);
//Math:ceil、floor、round、max、min、random、PI
var a = 0.1456*100;
console.log(a);

//4、频繁需要精确计算,利用第1种方法的思想自定义运算函数

3.数值范围

由于内存的限制, ECMAScript 并不能保存世界上所有的数值。 ECMAScript 能够表示的最小数值(绝对值)保存在 Number.MIN_VALUE 中——在大多数浏览器中,这个值是 5e-324;能够表示的最大数值(绝对值)保存在Number.MAX_VALUE 中——在大多数浏览器中,这个值是 1.7976931348623157e+308。

//Infinity
			console.log(  Number.MAX_VALUE*100  );//Infinity
			
			var test=Infinity;
			console.log( isFinite(test) );//false
			console.log( isFinite(123) );//true
			

4.NAN

NaN,即非数值(Not a Number)是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。例如,在其他编程语言中,任何数值除以 0 都会导致错误,从而停止代码执行。但在 ECMAScript 中,任何数值除以 0 会返回 NaN①,因此不会影响其他代码的执行。

            //NaN:not a number
			console.log(100/0);//Infinity
			console.log(0/0);//NaN
			console.log(typeof NaN);"number"
			
			//1\NaN与任何数运算结果都是NaN 
			//2\NaN与任何值都不想等,与自身都不相等
			console.log(NaN/100);
			console.log(NaN==NaN);//false
			console.log(NaN===NaN);//false
			
			console.log( isNaN(100) );//false

在基于对象调用 isNaN()函数时,会首先调用对象的 valueOf()方法,然后确定该方法返回的值是否可以转换为数值。如果不能,则基于这个返回值再调用 toString()方法,再测试返回值。

		<script type="text/javascript">
			var str="dj"
			console.log( +str );//NaN
			console.log(-(-str));//NaN
			console.log(str*1,str/1);
			
			
			//Number对于字符串转换,规则是不允许字符串里出现其他非数字字符,
			// 可以忽略前导后导空格,中间空格无法忽略
			console.log( Number("  012  30  ") );
			console.log( Number("") );//0
			
			//Number对于引用类型 ,引用类型优先调用valueof 方法,再调用toString 方法
			console.log( Number({}) );//NaN,[]---0,[100]---100
			
			console.log( ({}).valueOf() )//{}
			console.log( ({}).toString() );//"[object Object]"
			
			//Number对于Boolean类型,true--1 ,false--0
			//Number对于null---0,对于undefined---NaN
			
			
		</script>

5.转换成数字类型

+(加)、-(减)、*(乘)、/(除),会把类型转换成数字。

另有 3 个函数可以把非数值转换为数值: Number()、 parseInt()和 parseFloat()。第一个函数,即转型函数 Number()可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。这 3 个函数对于同样的输入会有返回不同的结果。

Number()函数的转换规则如下。

  • 如果是数字值,只是简单的传入和返回。

  • 如果是 Boolean 值, true 和 false 将分别被转换为 1 和 0。

  • 如果是 null 值,返回 0。

  • 如果是 undefined,返回 NaN。

  • 如果是字符串,遵循下列规则:

    • 如果字符串中只包含数字(包括前面带正号或负号的情况),则将其转换为十进制数值,即"1"会变成 1, "123"会变成 123,而"011"会变成 11(注意:前导的零被忽略了);
    • 如果字符串中包含有效的浮点格式,如"1.1",则将其转换为对应的浮点数值(同样,也会忽略前导零);
    • 如果字符串中包含有效的十六进制格式,例如"0xf",则将其转换为相同大小的十进制整数值;
    • 如果字符串是空的(不包含任何字符),则将其转换为 0;
    • 如果字符串中包含除上述格式之外的字符,则将其转换为 NaN。
  • 如果是对象,则调用对象的 valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是 NaN,则调用对象的 toString()方法,然后再次依照前面的规则转换返回的字符串值。

    由于 Number()函数在转换字符串时比较复杂而且不够合理,因此在处理整数的时候更常用的是parseInt() 函数。 parseInt()函数在转换字符串时,更多的是看其是否符合数值模式。它会忽略字符串前面的空格,直至找到第一个非空格字符。如果第一个字符不是数字字符或者负号, parseInt()就会返回 NaN;也就是说,用 parseInt()转换空字符串会返回 NaN(Number()对空字符返回 0) 。如果第一个字符是数字字符, parseInt()会继续解析第二个字符,直到解析完所有后续字符或者遇到了一个非数字字符。例如, "1234blue"会被转换为 1234,因为"blue"会被完全忽略。类似地, "22.5"会被转换为 22,因为小数点并不是有效的数字字符。

<script type="text/javascript">
			var str = "123.3abc";
			console.log(Number(str)); //NaN

			//字符串转成number类型
			//parseInt parseFloat :
			//从前往后读,取符合整数形式 / 小数形式console的数字
			console.log(parseInt(str)); //12
			console.log(parseFloat(str)); //12
			console.log(parseInt("")); //NaN

			console.log(parseInt("100", ""));//100
			console.log(parseInt(3, 8), parseInt(3, 2), parseInt(3, 0));
			//  3    NaN     3

			var num1 = parseInt("1234blue"); // 1234
			var num2 = parseInt(""); // NaN,与parseInt与Number的处理结果不一样
			var num3 = parseInt("0xA"); // 10(十六进制数转换成十进制数)
			var num4 = parseInt(22.5); // 22
			var num5 = parseInt("070"); //70(es5), 56(八进制数转换成十进制数-es3),
			var num6 = parseInt("70"); // 70(十进制数)
			var num7 = parseInt("0xf"); // 15(十六进制数转换成十进制数)
			console.log(num1, num2, num3, num4, num5, num6, num7);

			var num11 = parseFloat("1234blue");//1234
			var num12 = parseFloat("0xA");//0
			var num13 = parseFloat("22.5");//22.5
			var num14 = parseFloat("22.34.5");//22.34
			var num15 = parseFloat("0908.5");//908.5
			var num16 = parseFloat("3.125e7");//31250000
			console.log(num11, num12, num13, num14, num15, num16);

		</script>

3.6 String类型

字符串可以由双引号(")或单引号(’)表示。

var firstName = "Nicholas";
var lastName = 'Zakas';

1.字符字面量字符串

字 面 量含 义
\n换行
\t制表
\b空格
\r回车
\f进纸
\斜杠
单引号(’),在用单引号表示的字符串中使用。例如: ‘He said, ‘hey.’’
"双引号("),在用双引号表示的字符串中使用。例如: “He said, “hey.””
            // HTML的转义字符:
			// &nbsp;  &lt;  &gt;

			// 引号引起来的就是字符串string

			console.log("</scr" + "ipt>结束标签");

			// js的转义字符:  \   \n
			console.log("<\/script>结束标签");

			console.log("换行符\nbgy");

			// 字符串长度length
			var text = "this \55gtyt.";
			console.log(text.length); //11
			
			var mystr="花花";
			console.log(mystr.length);//2

2.字符串的特点

字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串填充该变量。

//字符串的值一旦创建就不可更改,浏览器存在垃圾机制
			var lang = "java";
			lang = lang + "script";
			console.log(lang); //JavaScript

3.转换为字符串

要把一个值转换为一个字符串有两种方式。第一种是使用几乎每个值变量都有的 toString()方法。

            //1. toString(),除了null和undefined,其他类型都可以用

			console.log((true).toString());//true
			console.log( (function(){}) .toString() );
			console.log( ({name:"huahua"}).toString());
			

			//2.所有值都可以用的方法,运行规则,优先调用该值的toString();
			//null--"null";  undefined--"undefined"

			console.log(String);//ƒ String() { [native code] }
			console.log(String(null));//null

es6 语法转换–多行字符串、模板字符串:

           var student="花花";login="登录";
			document.write(student+"同学,欢迎你"+login+"该系统");
			
			// es6的模板字符串  ${}
			document.write(`<br>${student}同学,欢迎您${login}该系统`);
			
			// 反引号内部的空格和换行符会被保留
			console.log(`budbuydb`b hg vfhv h`);
			
			//单引号双引号的字符串中间不允许换行的
			// console.log("fksh 
			// \cjsbk")
			
			// 字符串总长度length属性,字符串索引从0开始自然数递增
			var str="hello world";
			console.log(str[str.length-1]);

4.length

字符串变量获取字符串长度:str.length;

5.indexOf

字符串的索引从0开始,第一个字符的位置是0,第二个是1,以此类推。str[2]表示字符串str的第三个字符。

        // 字符串.indexOf(str,start) (字符串,起始位置)
			var str = "恭喜您,彩票中奖了,中奖一百万!";
			console.log(str.indexOf("中奖", 7));

			// 查找某个字符串原在字符串中第二次出现的位置
			console.log(str.indexOf("中奖", str.indexOf("中奖") + 1));
			//如果原字符串里没有对应字符,则返回值为-1
			console.log( str.indexOf("200"));//-1

6.substring

substring() 方法用于提取字符串中介于两个指定下标之间的字符。

            // 字符串.substring(start,stop ) 
			// (开始截取的索引位置,一直截取到stop-1的索引位置)
			// 最终截取的长度为stop-start
			// 能自动交换两个参数的位置
			console.log(str.substring(5, 11));    
           // 字符串.slice(start,end), start、end允许负值
			// 不能自动交换两个参数的位置
			// 从右往左数,字符串索引是从-1开始
			console.log(str.slice(-2,-1));
			
			// 字符串.substr(start,length)
			console.log(str.substr(2,10));

7. trim

从字符串中移除前导空格、尾随空格和行终止符.

移除的字符包括空格、制表符、换页符、回车符和换行符。

var message = "    abc def     \r\n  ";

document.write("[" + message.trim() + "]");
document.write("<br/>");
document.write("length: " + message.trim().length);

// 对应输出:
//  [abc def]
//  length: 7

8. charAt

charAt() 方法可返回指定位置的字符。

第一个字符位置为 0, 第二个字符位置为 1,以此类推.

语法:

string.charAt(index)

返回字符串中的最后一个字符:

var str = "HELLO WORLD";
var n = str.charAt(str.length-1);

3.7 Object类型

创建 Object 类型的实例并为其添加属性和(或)方法,就可以创建自定义对象.

var person = { 
		name:'张三', 
		age:24,
		say:function(){ 
			console.log('我要讲两句了');
		 }
 } 

3.7.1 包装对象

只要引用了字符串s的属性,JavaScript就会将字符串值通过调用new String(s)的方式转换成对象,这个对象继承了字符串(String)对象的方法,并被用来处理属性的引用。一旦属性引用结束,这个新创建的对象就会被销毁

var s = "test";
s.len = 4;//给它设置一个属性
var t = s.len;

最后t的值是undefined 。想知道为什么请看继续看解析:

这里第二行代码只是创建了一个临时字符串对象,并给len属性赋值为4,随即销毁这个对象。而第三行又是通过原始字符串s创建一个新字符串对象(这个不是第二行代码创建的对象,第二行代码创建的对象已经被销毁了)并尝试读取其len属相,这个属性自然不存在,因此表达式的结果为undefined。这段代码说明了在读取字符串、数字和布尔值的属性值或方法(实际上是它们对应包装对象的属性值或方法)表现的像对象一样。但如果你试图给属性赋值,则会忽略这个操作:修改只是发生在临时对象身上,而这个临时对象并不会继续保留下来。

3.8 原始与引用类型

  • 不可变的原始值

    – 任何方法都无法更改一个原始类型的值:数字、字符串、布尔值、null、undefined;

    – 对字符串类型的修改操作,只会返回一个新的字符串,原始字符串不会被修改;

    – 原始值在做比较时,只要值相等,他们就是相等的。

  • 可变的对象引用:数组、对象、函数等

    – 对象(引用)类型的值是可以修改的,看代码:

    原始变量名也在栈地址里,原始值存在栈数据里;

    引用变量名在栈地址里,引用值存在堆数据里,而栈数据里再存储堆地址。

           // 1.原始类型存储于栈内存
			// 引用内容存储于堆内容
			//
			// 2.原始类型的值是不可更改的
			var str = "hguv";
			console.log(str.substring(2, 3)); // u
			console.log(str); // hguv

			var arr = [1, 2, 3];
			arr.push(100);
			console.log(arr); // (4) [1, 2, 3, 100]

			// 3.全等符判断:原始类型只要值相等,他们就是相等的
			var bol1 = true,
				bol2 = false;
			console.log(bol1 === bol2); //  false

			var obj1 = {},
				obj2 = {};
			console.log(obj1 === obj2); // false

			var arr1 = ["a", "b", "c"];
			arr2 = arr1;
			arr2.push("d");

			console.log(arr1); // (4) ["a", "b", "c", "d"]

			var myobj1 = {
				name: "花花",
				age: 28,
				gender: "male",
			}
			var myobj2 = myobj1;
			myobj2.name = "勋勋";
			myobj1 = {
				name: "花花",
			}
			console.log(myobj1.name, myobj2.name); // 花花 勋勋
			console.log(myobj1 === myobj2); // false

			//包含相同属性及相同值的两个对象类型的值是不相等的
			var s1 = new String("abc");
			var s2 = new String("abc");
			console.log(s1 == s2); // false

			//当且仅当他们引用同一个基对象时,才相等
			var a = [1, 2, 3];
			var b = a; //变量b也引用这个数组 
			b[2] = 9; //通过变量b来修改引用的数组,变量a也会被修改   
			console.log(a === b); //true
			console.log(a); //控制台会打印:[1,2,9]

3.9 变量作用域

变量的作用域(scope) 是程序源代码中定义这个变量的区域。

作用域分为全局作用域和函数作用域(又叫局部作用域)两种。

全局作用域是最外围的一个执行环境,在web浏览器中,全局执行环境被认为是window对象。所有全局变量和函数都是作为window对象的属性和方法创建的。全局变量拥有全局作用域,在javascript代码中的任何地方都是有定义的。全局作用域直到应用程序退出例如关闭网页或浏览器时才会被销毁。

//在全局下写好的变量和方法就相当于在window 上追加属性
			var a=100;
			function fn(){
				console.log("myfn");
			}
			
			console.log(this.a);//全局下的this ,this===window
			window.fn();
			
			// console.log(b);//报错,b is not defined
			// console.log(window.b);//undefined,访问对象没有的属性,值为undefined

在函数内(var声明)的变量只在函数体内有定义。它们是局部变量,作用域是局部性的。函数参数也是局部变量,它们只在函数体内有定义。函数作用域中的所有代码执行完毕后,该作用域被销毁,保存在其中的所有变量和函数定义也随之销毁。

         // 局部变量,在函数体内用var 声明的变量,只能在函数体内使用
	     // 函数体内优先访问局部变量,找不到的情况下才逐层向外查找
		 // (函数体内可以访问函数体外的,函数体外不能访问函数体内的)
			var a=123;
			function test() {
				var b=123;
				var a=789;
				function demo() {
					var c=234;
					document.write(b);//123
					document.write(a);//789
				}//函数体内可以嵌套函数的
				demo();
				document.write(c);
			}
			test();

4、表达式

4.1 原始表达式

  1. 原始表达式不可再分割,是最小单位的表达式;简单的概念性东西,知道即可;

  2. 原始表达式包含直接量、关键字(保留字)和变量名;

//直接量
1;
1.02;
'hello world!';
//保留字
true;
false;
this;
//变量名
name;
age;

4.2组初始化表达式

  1. 可以简单理解为:数组直接量;

  2. 数组初始化表达式:由中括号([])和其内用逗号(英文状态 ,)分隔开的列表构成;

  3. 初始化的结果是创建一个新的数组;数组的元素是逗号分隔开的表达式的值;

  4. “数组初始化表达式”中的“元素”也可以是“数组初始化表达式”,即多维数组

//数组初始化表达式
[];
[1,2,3,4,'a','b','c'];
[['a','b','c'],['d','e','f'],['g','h']];
//中间省略的元素会怎样?最后省略的元素又会怎样?
[1,,,,6,,,] 

4.3 对象初始化表达式

  1. 可以简单理解为:对象直接量;
  2. 对象初始化表达式:由花括号({})和其内用逗号(英文状态 ,)分隔开的列表构成;
  3. 初始化的结果是创建一个新的对象; 对象的属性是逗号分隔开的表达式的值; 属性包括属性名和属性值,属性名和属性值之间用冒号隔开;
  4. “对象初始化表达式”中的“元素”也可以是“对象初始化表达式”,可以任意层级嵌套;
//对象初始化表达式
{name:'yourname', age:22};

{
    p1:{name:'华子', age:22},
    p2:{name:'华耀', age:25},
    p3:{name:'华赓', age:24}
};

4.4 函数定义表达式

函数定义表达式定义一个JavaScript函数

  1. 可以简单理解为:函数直接量;
  2. 表达式的值是这个新定义的函数;
var fn = function (a, b) {
  return a+b;
}
fn()

//函数声明
//function fn(a, b) {
//  return a+b;
//} 
//fn(); 

4.5 属性访问表达式

属性访问表达式得到一个对象的属性或者一个数组的元素值

  1. 有两种访问方式:点(.)、中括号([]);
  2. 点的访问方式更方便,中括号的访问方式使用所有情况;
var a = {name:'yourname', age:22};
var b = {
      p1:{name:'华子', age:22},
      p2:{name:'华耀', age:25},
      p3:{name:'华赓', age:24}
};
var c = [1,2,3,4,'a','b','c'];
var d = [['a','b','c'],['d','e','f'],['g','h']];
console.log(a.age);
console.log(b.p2['name']); 		//对象用中括号访问时,注意加引号!
console.log(c[4]); 
console.log(d[2][1]); 

4.6 调用表达式

调用表达式fn() 是执行函数或方法的语法表示;

  1. 如果调用的函数或方法有返回值,则表达式的值就是该返回值;
  2. 如果调用的函数或方法没有返回值,则表达式的值是undefined;
  3. 简单理解就是函数或方法名称后面跟上小括号代表执行;
fn(1, 2);
Math.max(1,2,3);//3
a.sort();

4.7 对象创建表达式

由函数调用表达式延伸而来,前面加个new即可;

  1. 如果不需要传递参数,函数后面的小括号可以省略;

    不看个例子感觉不踏实:

    new Array(1,2,3);	//数组长度为3,元素为[1,2,3]
    new Array(3)		//数组长度为3,元素为空[,,,]
    new String('hello world!');
    Number()/Boolean()/Object()/Function()
    

4.8 算术表达式

加减乘除取余,+ - * / % ;

+:数字相加 或 字符串连接;

加法操作的行为表现:

一、如果其中一个操作数是对象,则JavaScript会自动把他转成原始类型的值;

二、如果其中一个操作数是字符串的话,则另一个也会转成字符串;

三、如果两个操作数都是数字,则进行加法运算;

            console.log(1 + 5); //6
			console.log('1' + 5);//15
			console.log(new Date() + '--ok'); //Wed Jul       24 2019 18:51:46 GMT+0800 (中国标准时间)--ok
			console.log(12 + NaN); //NaN
			console.log(true + true); //2
			console.log(201 + null); //201
			console.log(203 + undefined);  //NaN
			console.log(3 + 5 + '猜猜看'); //8猜猜看
			//加法运算的结果要么是数字要么是字符串

减乘除取余----运算结果全都是number类型.

5、操作符

5.1 一元操作符

1. 递增和递减操作符

递增和递减操作符直接借鉴自 C,而且各有两个版本:前置型和后置型。顾名思义,前置型应该位于要操作的变量之前,而后置型则应该位于要操作的变量之后。

var age = 29;
++age;//age = age + 1;
var age = 29;
--age;//age=age-1
var age = 29;
var anotherAge = --age + 2;
alert(age);//28 
alert(anotherAge); //30

2.一元加和减操作符

一元加操作符以一个加号(+)表示,放在数值前面,对数值不会产生任何影响。

var num = 25;
num = +num; // 仍然是 25

布尔值 false 和 true 将被转换为 0 和 1,字符串值会被按照一组特殊的规则进行解析,而对象是先调用它们的 valueOf()和(或) toString()方法,再转换得到的值。

var s1 = "2";
			var s2 = "z";
			var b = false;
			var f = 1.1;
			var o = {
				valueOf: function() {
					return -1;
				}
			};
			s1++; 
			s2++;//s2=s2+1
			b++;  
			f--;  
			o--; //o=o-1
			console.log(s1,s2,b,f,o);//3 NaN 1 0.10000000000000009 -2
			

5.2 位操作符

1. 按位非(NOT)

按位非操作符由一个波浪线(~)表示,执行按位非的结果就是返回数值的反码。

var num1 = 10; 		// 二进制 0000 0000 0000 0000 0000 0000 0000 1010
var num2 = ~num1; 	// 二进制 1111 1111 1111 1111 1111 1111 1111 0101
alert(num2); 		// -11

// 负数以补码存储,换算十进制过程如下
// 第一步,减一
// 11111111111111111111111111110100
// 第二步,取反
// 10000000000000000000000000001011
// 第三步,十进制换算
// -11

2.按位与(AND)

按位与操作符由一个和号字符(&)表示,它有两个操作符数。

简而言之,按位与操作只在两个数值的对应位都是 1 时才返回 1,任何一位是 0,结果都是 0。

var result = 25 & 3;
alert(result); //1

对 25 和 3 执行按位与操作的结果是 1。

25  = 0000 0000 0000 0000 0000 0000 0001 1001
3   = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
AND = 0000 0000 0000 0000 0000 0000 0000 0001

3. 按位或(OR)

按位或操作符由一个竖线符号(|)表示,同样也有两个操作数。

按位或操作在有一个位是 1 的情况下就返回 1, 而只有在两个位都是 0 的情况下才返回 0。

var result = 25 | 3;
alert(result); 

25 与 3 按位或的结果是 27:

25 = 0000 0000 0000 0000 0000 0000 0001 1001
3  = 0000 0000 0000 0000 0000 0000 0000 0011
--------------------------------------------
OR = 0000 0000 0000 0000 0000 0000 0001 1011

4. 按位异或

按位异或操作符由一个插入符号(^)表示,也有两个操作数。

按位异或与按位或的不同之处在于,这个操作在两个数值对应位上只有一个 1 时才返回 1,如果对应的两位都是 1 或都是 0,则返回 0。

var result = 25 ^ 3;
alert(result); 

25 与 3 按位异或的结果是 26。

25  = 0000 0000 0000 0000 0000 0000 0001 1001
3   = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
XOR = 0000 0000 0000 0000 0000 0000 0001 1010

5. 左移

左移操作符由两个小于号(<<)表示,这个操作符会将数值的所有位向左移动指定的位数。例如,如果将数值 2(二进制码为 10)向左移动 5 位,结果就是 64(二进制码为 1000000)。

var oldValue = 2; // 等于二进制的 0000 0000 0000 0000 0000 0000 0000 0010

var newValue = oldValue << 5; // 等于二进制的 1000000,十进制的 64

注意:

a. 在向左移位后,原数值的右侧多出了 5 个空位。左移操作会以 0 来填充这些空位,以便得到的结果是一个完整的 32 位二进制数。

b. 左移不会影响操作数的符号位。换句话说,如果将-2 向左移动 5 位,结果将是-64,而非 64。

6. 有符号右移

有符号的右移操作符由两个大于号(>>)表示,这个操作符会将数值向右移动,但保留符号位(即正负号标记)。有符号的右移操作与左移操作恰好相反,即如果将 64 向右移动 5 位,结果将变回 2:

var oldValue = 64; // 等于二进制的 0000 0000 0000 0000 0000 0000 0100 0000

var newValue = oldValue >> 5; // 等于二进制的 10 ,即十进制的 2

7. 无符号右移

无符号右移操作符由 3 个大于号(>>>)表示,这个操作符会将数值的所有 32 位都向右移动。

首先,无符号右移是以 0 来填充空位,而不是像有符号右移那样以符号位的值来填充空位。

所以,对正数来说,无符号右移的结果与有符号右移相同。仍以前面有符号右移的代码为例,如果将 64 无符号右移 5 位,结果仍然还是 2:

var oldValue = 64; // 等于二进制的 1000000
var newValue = oldValue >>> 5; // 等于二进制的 10 ,即十进制的 2

但对负数的结果就不一样了,由于负数以其绝对值的二进制补码形式表示,因此就会导致无符号右移后的结果非常之大,如下面的例子所示:

var oldValue = -64; // 等于二进制的 11111111111111111111111111000000
var newValue = oldValue >>> 5; //  00000111111111111111111111111110 等于十进制的 134217726

5.3 逻辑操作符

1. 逻辑非

逻辑非操作符由一个叹号(!)表示。

这个操作符都会返回一个布尔值。逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反。也就是说,逻辑非操作符遵循下列规则:

  • 如果操作数是一个非空(null)对象,返回 false;
  • 如果操作数是一个空字符串,返回 true;
  • 如果操作数是一个非空字符串,返回 false;
  • 如果操作数是数值 0,返回 true;
  • 如果操作数是任意非 0 数值(包括 Infinity),返回 false;
  • 如果操作数是 null,返回 true;
  • 如果操作数是 NaN,返回 true;
  • 如果操作数是 undefined,返回 true。
alert(!false); 
alert(!"blue"); 
alert(!0); 
alert(!NaN); 
alert(!""); 
alert(!12345);

2. 逻辑与

逻辑与操作符由两个和号(&&)表示,有两个操作数。

console.log(123 && null); //null

逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况下,逻辑与操作就不一定返回布尔值;此时,它遵循下列规则:

  • 如果第一个操作数是对象,则返回第二个操作数;
  • 如果第二个操作数是对象,则只有在第一个操作数的求值结果为 true 的情况下才会返回该对象;
  • 如果两个操作数都是对象(或非0数),则返回第二个操作数;
  • 如果有一个操作数是 null,则返回 null;
  • 如果有一个操作数是 NaN,则返回 NaN;
  • 如果有一个操作数是 undefined,则返回 undefined;
  • 如果有一个操作数是 0 ,则返回0;
var found = false;
var result = (found && someUndefinedVariable); 
alert(result);

3. 逻辑或

逻辑或操作符由两个竖线符号(||)表示,有两个操作数.

与逻辑与操作相似,如果有一个操作数不是布尔值,逻辑或也不一定返回布尔值;此时,它遵循下列规则:

  • 如果第一个操作数是对象,则返回第一个操作数;
  • 如果第一个操作数的求值结果为 false,则返回第二个操作数;
  • 如果两个操作数都是对象(或非0数),则返回第一个操作数;
  • 如果两个操作数都是 null,则返回 null;
  • 如果两个操作数都是 NaN,则返回 NaN;
  • 如果两个操作数都是 undefined,则返回 undefined。
var found = true;
var result = (found || someUndefinedVariable); 
alert(result); 

变量 someUndefinedVariable 也没有定义。但是,由于变量 found的值是 true,而变量 someUndefinedVariable 永远不会被求值,因此结果就会输出"true"。

           console.log( ""||"abc" ); //abc
			console.log( ({}) || "123" ); // {}

			var c = (a = 3) || (b = 4);
			console.log(a); //3
			 console.log(b); // 报错,b is not defined
			console.log(c); //3 

5.4 乘性操作符

ECMAScript 定义了 3 个乘性操作符:乘法、除法和求模。

如果参与乘性计算的某个操作数不是数值,后台会先使用 Number()转型函数将其转换为数值。也就是说,空字符串将被当作0,布尔值 true 将被当作 1。

1. 乘法

乘法操作符由一个星号(*)表示,用于计算两个数值的乘积。

在处理特殊值的情况下,乘法操作符遵循下列特殊的规则:

  • 如果操作数都是数值,执行常规的乘法计算,即两个正数或两个负数相乘的结果还是正数,而如果只有一个操作数有符号,那么结果就是负数。如果乘积超过了 ECMAScript 数值的表示范围,则返回 Infinity 或-Infinity;

  • 如果有一个操作数是 NaN,则结果是 NaN;

  • 如果是 Infinity 与 0 相乘,则结果是 NaN;// Infinity*0=NaN

  • 如果是 Infinity 与非 0 数值相乘,则结果是 Infinity 或-Infinity,取决于有符号操作数的符号;

  • 如果是 Infinity 与 Infinity 相乘,则结果是 Infinity;

  • 如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值,然后再应用上面的规则。

    // 乘性操作符 *  /  %
    			// 操作出来的结果都是number类型
    			// 如果遇到操作数不是number的类型,隐式转换为number类型在做计算
    
    			console.log("123" * 2);
    

2. 除法

除法操作符由一个斜线符号(/)表示,执行第二个操作数除第一个操作数的计算。

var result = 66 / 11;

与乘法操作符类似,除法操作符对特殊的值也有特殊的处理规则。这些规则如下:

  • 如果操作数都是数值,执行常规的除法计算,即两个正数或两个负数相除的结果还是正数,而如果只有一个操作数有符号,那么结果就是负数。如果商超过了 ECMAScript 数值的表示范围,则返回 Infinity 或-Infinity;

  • 如果有一个操作数是 NaN,则结果是 NaN;

  • 如果是 Infinity 被 Infinity 除,则结果是 NaN; // Infinity / Infinity = NaN

  • 如果是零被零除,则结果是 NaN;

  • 如果是非零数被零除,则结果是 Infinity 或-Infinity,取决于有符号操作数的符号;

  • 如果是 Infinity被任何数值除,则结果是Infinity 或-Infinity,取决于有符号操作数的符号;

    // Infinity/0=Infinity

  • 如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值,然后再应用上面的规则。

3. 求模

求模(余数)操作符由一个百分号(%)表示.

var result = 26 % 5; // 1

与另外两个乘性操作符类似,求模操作符会遵循下列特殊规则来处理特殊的值:

  • 如果操作数都是数值,执行常规的除法计算,返回除得的余数;
  • 如果被除数是无穷大值Infinity 而除数是有限大的数值,则结果是 NaN;// Infinity%?=NaN
  • 如果被除数是有限大的数值而除数是零,则结果是 NaN;//5%0=NaN
  • 如果是 Infinity 被 Infinity 除,则结果是 NaN; // Infinity % Infinity = NaN
  • 如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数; // 5%Infinity = 5
  • 如果被除数是零,则结果是零;//0%?=0
  • 如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值,然后再应用上面的规则

5.5 加性操作符

与乘性操作符类似,加性操作符也会在后台转换不同的数据类型。

1. 加法

如果两个操作符都是数值,执行常规的加法计算,然后根据下列规则返回结果:

  • 如果有一个操作数是 NaN,则结果是 NaN;
  • 如果是 Infinity 加 Infinity,则结果是 Infinity;
  • 如果是-Infinity 加-Infinity,则结果是-Infinity;
  • 如果是 Infinity 加-Infinity,则结果是 NaN;// Infinity +(- Infinity )=NaN
  • 如果是+0 加+0,则结果是+0;
  • 如果是-0 加-0,则结果是-0;
  • 如果是+0 加-0,则结果是+0。//+0-0=+0

不过,如果有一个操作数是字符串,那么就要应用如下规则:

  • 如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来;
  • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接起来;如果有一个操作数是对象、数值或布尔值,则调用它们的 toString()方法取得相应的字符串值,然后再应用前面关于字符串的规则。对于 undefined 和 null,则分别调用 String()函数并取得字符串"undefined"和"null"。

2. 减法

与加法操作符类似, ECMAScript 中的减法操作符在处理各种数据类型转换时,同样需要遵循一些

特殊规则,如下所示:

  • 如果两个操作符都是数值,则执行常规的算术减法操作并返回结果;
  • 如果有一个操作数是 NaN,则结果是 NaN;
  • 如果是 Infinity 减 Infinity,则结果是 NaN;//infinity-infinity=NaN
  • 如果是-Infinity 减-Infinity,则结果是 NaN;
  • 如果是 Infinity 减-Infinity,则结果是 Infinity;
  • 如果是-Infinity 减 Infinity,则结果是-Infinity;
  • 如果是+0 减+0,则结果是+0;
  • 如果是+0 减-0,则结果是-0;//+0-(-0)=(-0)
  • 如果是-0 减-0,则结果是+0;
  • 如果有一个操作数是字符串、布尔值、 null 或 undefined,则先在后台调用 Number()函数将其转换为数值,然后再根据前面的规则执行减法计算。如果转换的结果是 NaN,则减法的结果就是 NaN;
  • 如果有一个操作数是对象,则调用对象的 valueOf()方法以取得表示该对象的数值。如果得到的值是 NaN,则减法的结果就是 NaN。如果对象没有 valueOf()方法,则调用其 toString()方法并将得到的字符串转换为数值。
            // 加号: + , 操作出来的结果是字符串/数字
			// 减法: - , 操作出来的结果都是number 类型
			
			var result1 = 5 - true; //4
			var result2 = NaN - 1; // NaN
			var result3 = 5 - 3;  //2
			var result4 = 5 - ""; // 5
			var result5 = 5 - "2";  //3
			var result6 = 5 - null; // 5
			console.log(result1,result2,result3,result4,result5,result6);

5.6 关系操作符

关系运算符用于检测两个值之间的关系,总是返回一个布尔值true或false。

5.6.1 比较运算符

小于(<) 、大于(>) 、小于等于(<=)和大于等于(>=)这几个关系操作符用于对两个值进行比较。

当关系操作符的操作数使用了非数值时,也要进行数据转换或完成某些奇怪的操作。以下就是相应的规则。

  • 如果两个操作数都是数值,则执行数值比较。
  • 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值。
  • 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较。
  • 如果一个操作数是对象,则调用这个对象的 valueOf()方法,用得到的结果按照前面的规则执行比较。如果对象没有 valueOf()方法,则调用 toString()方法,并用得到的结果根据前面的规则执行比较。
  • 如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。
     /*  关系运算符,操作结果都是true/false
			 1.比较运算符 > >=  < <=
			优先进行数字值比较,当两边都是字符串时才进行字符编码比较
			任何值与NaN 比较,结果都是false
		*/
			console.log("123">=123); // true
			console.log("abc">"abbc"); // false 按位比较字符编码
			
			//字符串比较大小
			var result = "Brick" < "alphabet";//true
			//.toUpperCase()   或者  .toLowerCase()
			var result1 = ("Brick").toUpperCase()< ("alphabet").toUpperCase();//false
      /* 			

5.6.2 相等运算符

1. 相等和不相等

在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:

  • 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false 转换为 0,而true 转换为 1;
  • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
  • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法,用得到的基本类型值按照前面的规则进行比较;

这两个操作符在进行比较时则要遵循下列规则。

  • null 和 undefined 是相等的。
  • 要比较相等性之前,不能将 null 和 undefined 转换成其他任何值(null == 0为false)。
  • 如果有一个操作数是 NaN,则相等操作符返回 false,而不相等操作符返回 true。重要提示:即使两个操作数都是 NaN,相等操作符也返回 false;因为按照规则, NaN 不等于 NaN。NaN!=r任何–>
  • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true;否则,返回 false。

2.全等和不全等

全等操作符由 3 个等于号(===)表示,它只在两个操作数未经转换就相等的情况下返回 true。

不全等操作符由一个叹号后跟两个等于号(!==)表示,它在两个操作数未经转换就不相等的情况下返回 true。

null == undefined 会返回 true,因为它们是类似的值;但 null === undefined 会返回 false,因为它们是不同类型的值。

5.6.3 in运算符

in运算符:检查右侧对象里面是否拥有左侧属性名,如果有返回true;反之,返回false。

     // 3. in 操作符,检查某个对象是否有指定属性,
			//     可以检查自有属性也可以检查继承得到的属性
			var a = {x:1, y:2, z:''};
			console.log('x' in a); //true
			console.log('z1' in a); // false
			console.log('toString' in a); //true

5.6.4 instanceof运算符

检查左侧的对象是否是右侧类的实例,如果是返回true;

如果一个对象是一个“父类”的子类的实例,则一样返回true。

// 4.instanceof 操作符,检查某个对象是不是某个函数的实例,
			//    所有引用类型都是 Object的实例
			var d = new String();
			console.log(d instanceof Date); //false
			console.log(d instanceof Array); //false
			console.log(d instanceof String); //true
			console.log(d instanceof Object); //true

5.8 条件操作符(三目运算)

variable = boolean_expression ? true_value : false_value;

这行代码的含义就是基于对 boolean_expression 求值的结果,决定给变量 variable赋什么值。如果求值结果为 true,则给变量 variable 赋 true_value 值;如果求值结果为 false,则给变量 variable 赋 false_value 值。

var max = (num1 > num2) ? num1 : num2;

var myVar=("abc")?1:true;

在这个例子中, max 中将会保存一个最大的值。这个表达式的意思是:如果 num1 大于 num2(关系表达式返回 true),则将 num1 的值赋给 max;如果 num1 小于或等于 num2 (关系表达式返回 false),则将 num2 的值赋给 max。

5.9 赋值操作符

简单的赋值操作符由等于号(=)表示,其作用就是把右侧的值赋给左侧的变量.

如果在等于号(=)前面再添加乘性操作符、加性操作符或位操作符,就可以完成复合赋值操作。

这种复合赋值操作相当于是对下面常规表达式的简写形式:

var num = 10;
num = num + 10;

其中的第二行代码可以用一个复合赋值来代替:

var num = 10;
num += 10;//num=num+10

每个主要算术操作符(以及个别的其他操作符)都有对应的复合赋值操作符:

  • 乘/赋值(*=);
  • 除/赋值(/=);
  • 模/赋值(%=);
  • 加/赋值(+=);
  • 减/赋值(-=);
  • 左移/赋值(<<=);var a=10;a<<=3;//1010000,80
  • 有符号右移/赋值(>>=);
  • 无符号右移/赋值(>>>=)。

5.10 逗号操作符

  • 逗号操作符多用于声明多个变量。

使用逗号操作符可以在一条语句中执行多个操作,如下面的例子所示:

console.log(num1,num2,num3)
var num1=1, num2=2, num3=3;

  • 逗号操作符还可以用于赋值。

在用于赋值时,逗号操作符总会返回表达式中的最后一项

var num = (5, 1, 4, 8, 0); // num 的值为 0

由于 0 是表达式中的最后一项,因此 num 的值就是 0。

5.11 表达式计算

eval()可以解释运行由JavaScript源代码组成的字符串,并产生一个值;如果你使用了eval(),你要确定你确实需要使用它。

console.log("1+2"); //1+2
console.log(eval("1+2")); // 3

eval()只有一个参数。

如果传入的参数不是字符串,它直接返回这个参数。

如果参数是字符串,它会把字符串当成JavaScript代码进行编译,如果编译失败则抛出一个语法错误异常。如果编译成功,则开始执行这一段代码,并返回字符串中的最后一个表达式会或语句的值,如果最后一个表达式或语句没有值,则最终返回undefined。如果字符串抛出一个异常,这个异常将把该调用传递给eval()。

//eval: 使用该函数的环境 即 该函数访问的变量的环境

var geval = eval; //使用别名geval调用eval将是全局eval
var x = "global",
	y = "global"; //global changed
function f() {
	var x = "local";
	eval("x += ' changed';");
	alert(x); //local changed
	return x; //            
}
function g() {
	var y = "local";
	geval("y += ' changed';"); //y=全局下的global	
	alert(y); // local
	return y;
}
console.log(f(), x); //local changed   global
console.log(g(), y); //local           global changed

6、语句

6.1 if语句

条件语句用于基于不同的条件来执行不同的动作。

在 JavaScript 中,我们可使用以下条件语句:

  • if 语句 - 只有当指定条件为 true 时,使用该语句来执行代码
  • if…else 语句 - 当条件为 true 时执行代码,当条件为 false 时执行其他代码
  • if…else if…else 语句- 使用该语句来选择多个代码块之一来执行
  • switch 语句 - 使用该语句来选择多个代码块之一来执行

1. if 语句

if (condition)
{
	*当条件为 true 时执行的代码*
}

2. if…else 语句

	/* 			
			if(条件一判断){
				//条件一为真 执行的语句块
			}
			else if(条件二判断){
				//条件二为真 执行的语句块
			}
			else if(条件三判断) else{
				所有条件都不满足时,默认执行的语句块
			}
	 */

3.if…else if…else 语句

使用 if…else if…else 语句来选择多个代码块之一来执行。

if (condition1)
{
    当条件 1true 时执行的代码
}
else if (condition2)
{
    当条件 2true 时执行的代码
}
else
{
  当条件 1 和 条件 2 都不为 true 时执行的代码
}
	var now = (new Date).getHours();
	if (now <= 12) {
	    document.write("早上好");
	} else if (now <= 18) {
	    document.write("下午好");
	} else {
		document.write("晚上好");
	}

4.if 语句嵌套

// if语句可以嵌套if语句
	if (now <= 18) {
		if (now <= 12) {
			document.write("早上好");
		} else {
			document.write("下午好");
		}
	} 
	else {
		document.write("晚上好");
	}

6.2 switch语句

switch 语句用于基于不同的条件来执行不同的动作。

使用 switch 语句来选择要执行的多个代码块之一。

switch (variabal) {
	case 条件1:语句块;
		break;
	case 条件2:语句块;
		break;
	default:
		不匹配条件时执行的语句块;
}

原理:首先设置表达式 n(通常是一个变量)。随后表达式的值会与结构中的每个 case 的值做比较。如果存在匹配,则与该 case 关联的代码块会被执行。请使用 **break **来阻止代码自动地向下一个 case 运行。

var week = (new Date).getDay();
switch(week){
	case 0: document.write("今天是周日");break;
	case 1: document.write("今天是周一");break;
	case 2: document.write("今天是周二");break;
	case 3: document.write("今天是周三");break;
	case 4: document.write("今天是周四");break;
	case 5: document.write("今天是周五");break;
	case 6: document.write("今天是周六");break;
	default:document.write("祝您心情愉快!");
}

default 关键词

使用 default 关键词来规定匹配不存在时做的事情:

// 让用户输入成绩:
// 90-100优秀 80-90良好 70-80中等 60-70及格 -60以下不及格

var userIn=prompt("请输入考试成绩");
switch(parseInt(userIn/10)){
	case 10:
	case 9:alert("优秀");break;
	case 8:alert("良好");break;
	case 7:alert("中等");break;
	case 6:alert("及格");break;
	default:alert("不及格");
}

6.3 for语句

JavaScript 支持不同类型的循环:

  • for - 循环代码块一定的次数
  • for/in - 循环遍历对象的属性
  • while - 当指定的条件为 true 时循环指定的代码块
  • do/while - 同样当指定的条件为 true 时循环指定的代码块
for(语句一;语句二;语句三){
				//代码块
}
var arr = [1, 2, 3];
			var i = 0;
			for (; i < arr.length; i++) {
				console.log(arr[i]);
			}
  • 语句 1

通常我们会使用语句 1 初始化循环中所用的变量 (var i=0)。

语句 1 是可选的,也就是说不使用语句 1 也可以。

您可以在语句 1 中初始化任意(或者多个)值:

  • 语句 2

通常语句 2 用于评估初始变量的条件。

语句 2 同样是可选的。

如果语句 2 返回 true,则循环再次开始,如果返回 false,则循环将结束。

	// 语句二可省,但是一定要添加控制条件,避免死循环
  • 语句 3

在每次循环(代码块)已被执行之后执行。

通常语句 3 会增加初始变量的值。

语句 3 也是可选的。

语句 3 有多种用法。增量可以是负数 (i–),或者更大 (i=i+15)。

语句 3 也可以省略(比如当循环内部有相应的代码时)

小练习:

/* 
		1、循环个数组var size=[1,2,3,4,5,6,7],输出数组的每个元素到页面上;
        2、写个乘法口诀表;
        3、百钱百鸡
         公鸡5文钱一只,
         母鸡3文钱一只,
         小鸡1文钱三只,
         用100文钱买100只鸡, 
         问,公鸡,母鸡,小鸡各几只?
 */

/*1、循环个数组var size=[1,2,3,4,5,6,7],输出数组的每个元素到页面上;  */
	var size = [1, 2, 3, 4, 5, 6, 7];
	for (var a = 0; a < size.length; a++) {
		document.write(size[a] + "<br />");
	}

/*2、写个乘法口诀表;  */
	var sum = 0;
	for (i = 1; i <= 9; i++) {
		for (j = 1; j <= i; j++) {
			sum = i * j;
			document.write(i +"*"+ j +"="+sum+"&nbsp");
		}
		document.write("<br/>");
	}

/*       3、百钱百鸡
         公鸡5文钱一只,x
         母鸡3文钱一只,y
         小鸡1文钱三只,z
         用100文钱买100只鸡, 
         问,公鸡,母鸡,小鸡各几只? 
*/
for (var x = 0; x <= 20; x++) {
	for (var y = 0; y <= 33; y++) {
		for (var z = 0; z <= 100 - x - y; z++) {
			if ((x + y + z === 100) && (5 * x + 3 * y + z / 3 === 100)) {
				document.write(`公鸡 ${x} 母鸡 ${y} 小鸡${z} <br>`);
			}
		}
	}
}

6.4 while语句

while 循环会在指定条件为真时循环执行代码块。

/* 			while(条件判断){
				语句块;
				
				break;
			}
	*/

小练习:

/* 
 假设某人有100,000现金。
每经过一次路口需要进行一次交费。交费规则为:
当他现金大于50,000时每次需要交5%;
如果现金小于等于50,000时每次交5,000。
请写一程序计算此人可以经过多少次这个路口。
*/
			var money = 100000,n=0;
			while (money >= 5000) {
				n++;
				if (money > 50000) {
					money = money * 0.95;
				} else {
					money -= 5000;
				}
			}
			document.write(`能经过${n}次路口,还剩${money}块`);
			//能经过23次路口,还剩3767.497911552964块

6.5 for…in语句

<p>点击下面的按钮,循环遍历对象 "person" 的属性及其值值。</p>
		<button onclick="myFunction()">点击这里</button>
		<p id="demo"></p>

		<script type="text/javascript">
			function myFunction() {
				var x;
				var txt = "";
				var person = {
					fname: "Bill",
					lname: "Gates",
					age: 56
				};
				for (x in person) {
					txt = txt + x + ":" + person[x] + "<br>";
				}
				document.getElementById("demo").innerHTML = txt;
			}
			/* fname:Bill
               lname:Gates
               age:56 
			 */

			var arr = [1, 2, 3, 4, 5];
			//arr[0]=1 arr[1]=2
			for (var i in arr) {
				console.log(i);   //01234
				// console.log(arr[i]);
			}

			var person = {
				name: "花花",
				age: 18,
				gender: "male",
				grade: 88,
				sayHello: function() {
					document.write("hello");
				}
			}
			var myarr = [],
				n = 0;
			for (myarr[n++] in person);

			console.log(myarr);
		/* 	Array(5)
			0: "name"
			1: "age"
			2: "gender"
			3: "grade"
			4: "sayHello"
			length: 5 */
		</script>

6.6 do-while语句

do/while 循环是 while 循环的变体。该循环会在检查条件是否为真之前执行一次代码块,然后如果条件为真的话,就会重复这个循环。

do {
				//代码块
			} while ( //判断条件)

使用 do/while 循环。该循环至少会执行一次,即使条件为 false 它也会执行一次,因为代码块会在条件被测试前执行:

function myFunction() {
				var x = "",
					i = 0;
				do {
					x = x + "该数字为 " + i + "<br>";
					i++;
				} while (i < 5)
				document.getElementById("demo").innerHTML = x;
			}
/* 
该数字为 0
该数字为 1
该数字为 2
该数字为 3
该数字为 4
*/

while 和 do/while 的区别

//条件为假, do while至少执行一次, 
//条件为真, while和do while执行次数一致

6.7 break和continue语句

break 语句用于跳出循环。

continue 用于跳过循环中的一个迭代,继续执行下一个迭代(如果有的话)。

// break:结束它所在的那层循环体
			// continue:结束它所在的那层循环体的 当前那一次循环
			for(var i=0;i<10;i++){
				if(i==3){
					continue;//break;
				}
				console.log(i);
			}
			console.clear();
			var n=0;
			for(var x=0;x<10;x++){
				for(var y=0;y<10;y++){
					if(y==5){
						continue;//90    break--50
					}
					n++;
				}
			}
			console.log(n);

6.8 label 语句

标签语句,在语句之前加上冒号:可标记 JavaScript 语句。

break 和 continue可以使用标签语句。

	// lebal名字:语句块
			var a = 0;
			forhere:
				for (var i = 0; i < 10; i++) {
					for (var j = 0; j < 10; j++) {
						if (j == 5) {
							continue forhere; //        break--5
							//体会continue后面加和不加语句标签的区别;
							//体会break后面加和不加语句标签的区别;
						}
						a++;
					}
				}
			console.log(a); //50

continue 语句(带有或不带标签引用)只能用在循环中。

break 语句(不带标签引用),只能用在循环或 switch 中。

通过标签引用,break 语句可用于跳出任何 JavaScript 代码块:


			cars = ["BMW", "Volvo", "Saab", "Ford"];
			list: {
				document.write(cars[0] + "<br>");
				document.write(cars[1] + "<br>");
				document.write(cars[2] + "<br>");
				break list;
				document.write(cars[3] + "<br>");
				document.write(cars[4] + "<br>");
				document.write(cars[5] + "<br>");
			}

6.9 return语句

return指定函数调用后的返回值。

所以,return只能在函数体内出现;return后面的代码不会再执行。

如果要对一个函数断点调试时,断点设置在return之后,对于函数调试无意义。

// return语句:只能放在函数体内,函数体内遇到return 立即终止执行函数,只能返回一个值
			function fn(){
				var n=0
				for(var x=0;x<10;x++){
					for(var y=0;y<10;y++){
						if(y==5){
							return x,y,n;//90    break--50
						}
						n++;
					}
				}
				
			}
			console.log( fn() );

6.10 throw抛出异常

立即停止正在执行的程序,跳转至就近的逻辑异常处理程序。

	// throw 抛出错误,阻塞后面的代码执行
	throw new Error("错误对象");
			
	function factorial(n) {
		if (isNaN(n)) throw new Error('请输入数字,HOHO');
		if (n == 1) return 1;
		return n * factorial(n - 1); //递归
	}
	var n = factorial(3); //3*factorial(2)--3*2*factorial(1)--3*2*1
	console.log(n); //6

	var n = factorial('a05');
	console.log(n);

	var n = factorial(5);
	console.log(n);

throw抛出异常该异常可以是 JavaScript 字符串、数字、逻辑值或对象。

6.11 try-catch-finally语句

try-catch-finally是JavaScript的异常处理机制。

try{
       //我们自认为没有错误的 处理业务的代码
    }
catch(e){
       //上面的业务处理代码报错了,这里才会执行
       //console.log(e);
    }
finally{
       //这里总是会执行的,领导总结发言
    }
	/* 	try..catch..finally 捕捉错误
			出现错误不阻塞后面代码的执行
			 */

			// var a = 90;
			try {
				console.log(a);
			} catch (e) {
				//返回错误类型  
				console.log(e.name); //ReferenceError
				//错误描述
				console.log(e.message); //a is not defined
			} finally {

				try {
					var b = 100;
					console.log('大会到此结束');//大会到此结束
				} catch (e) {
					console.log(e);
				}
			}
			console.log(b);//100

6.12 with语句

with语句用于临时扩展作用域链。

临时扩展作用域,临时;with代码块执行完成后恢复原始状态。

with(obj){
				//语句块
			}
//with 临时扩展语句块的作用域为obj对象
			with(document.querySelector("div").style){
				backgroundColor="pink";
				width="200px";
				height="200px";
				borderRadius="50%";
				margin="0 auto";
			}


不推荐使用with,代码不好优化、运行慢等问题;

并且,严格模式下是不能使用with。

7、对象

JavaScript 对象是拥有属性和方法的数据。

已经学习了 JavaScript 变量的赋值。

var car = "Fiat";

对象也是一个变量,但对象可以包含多个值(多个变量)。

var car = {
	type:"Fiat", 
	model:500, 
	color:"white"
};

3 个值 (“Fiat”, 500, “white”) 赋予变量 car,3 个变量 (type, model, color) 赋予变量 car。

7.1 对象基础

7.1.1 对象创建

1. 对象字面量

var car2={
	weight:"850kg",
	color:"black",
	name:"BMW",
	drive:function(){
		console.log("日趋千里");
}

对象字面量可以用来创建单个对象,但如果要创建多个对象,会产生大量的重复代码。

2. 工厂模式

该模式抽象了创建具体对象的过程,用函数来封装以特定接口创建对象的细节。


			// 2.工厂模式
			function createCar(w, c) {
				var car = {
					weight: w,
					color: c,
					name: "BMW",
					drive: function() {
						console.log("日趋千里");
					}
				}
				return car;
			}
			var car1 = createCar("850kg", "pink");
			var car2 = createCar("800kg", "white");
			console.log(car1 === car2); //false
			console.log(car1.weight == car2.weight); //true
			console.log(car1, car2);

3.构造函数模式

通过创建自定义的构造函数,来定义自定义对象类型的属性和方法。

该模式没有显式地创建对象,直接将属性和方法赋给了this对象,且没有return语句。

// 3.构造函数方式
			/* 	new 操作符调用函数过程:
				向堆内存申请一片空间,让this指向这片空间,
				再向这片空间增加属性并对属性赋值,最后再将this进行返回.
			*/
			function CreateCar(n, a, g) {
				//隐式var this={}
				this.name = n;
				this.age = a;
				this.gender = g;
				this.say = function() {
					console.log("吃喝玩乐");
				}
				// 隐式的return this
			}
			var person1 = new CreateCar("花花", "18", "男");
			var person2 = new CreateCar("勋勋", "19", "男");
			console.log(person1, person2);

4. new创建

new后面跟一个函数表示创建对象;

//4.new操作符调用宿主函数string Boolean number object date error
var obj1 = new Object();
console.log(obj1);
var obj2 = new Date();
console.log(obj2);
var obj3 = new Array();
console.log(obj3);

这里的函数是构造函数(constructor)。

5. Object.create()创建

Object.create()的参数只能是对象或null;

用于创建一个新对象,参数是这个对象的原型。


// 5.Object.create(),参数只能是对象或null,返回值为对象
	var obj = Object.create(new Boolean());
	console.log(typeof obj);//object
	console.log(obj);//Boolean {}
var b = Object.create(new Array(14,5,6,2,3,7));
console.log(b.length);


var nu = Object.create(new String('null'));
console.log(nu[2]);

var nu = Object.create(null);
console.log(nu[2]);

7.1.2 对象方法

1. valueOf()

valueOf()方法返回当前对象原始值。

object 引用是任何内部 JavaScript 对象,将通过不同的方式为每个内部 JavaScript 对象定义 valueOf 方法。

// 数组 new Object 函数,调用valueOf方法,函数返回值都是它本身
	var arr = [true, 100, [1, 2, 3]];
	console.log(arr.valueOf() === arr);

// new Boolean/new String/new Number
// 调用valueOf方法,返回的是原始值
	var num = new Number();
	console.log(typeof num);
	console.log(typeof num.valueOf());//"number"

	var time = new Date;
	console.log(typeof time);//object
			
	console.log(time.valueOf());

	console.log(Number(time)); //优先调用valueOf方法

2. toString()

toString()方法返回当前对象对应的字符串形式。

	// 数组调用toString,去掉中括号,数组的每一项再调用toString
			console.log(arr.toString());
			document.write(arr);

			// 函数调用toString(),返回函数体对应的字符串
			function myfn() {
				var a = 100;
				console.log(a);
			}
			document.write(myfn);

			// 对象{}调用toString(),返回[object object]
			var obj = {}
			document.write(obj);

			// new Boolean/new String/new Number
			// 调用toString(),返回字符串

toString 方法是一个所有内置的 JavaScript 对象的成员。 它的行为取决于对象的类型:

Object行为
数组将Array的元素转换为字符串,结果字符串被连接起来,用逗号分隔
布尔值如果布尔值为true,则返回"true",否则返回"false"
日期返回日期的文本表示形式
错误返回一个包含相关错误信息的字符串
函数返回如下格式的字符串,其中functionName是函数的名称 function functionName() { [native code] }
Number返回数字的文字表示形式
字符串返回String对象的值
默认{}返回"object Object"

附注:valueOf偏向于运算,toString偏向于显示。

1、 在进行强转字符串类型时将优先调用toString方法,强转为数字时优先调用valueOf。

2、 在有运算操作符的情况下,valueOf的优先级高于toString。

7.13对象属性

  • 增添属性
// 增添属性: 给对象没有的属性赋值
			obj.name="花花";
			obj.gender="男";
  • 删除属性

使用delete运算符可以删除对象属性。

只能删除自有属性,不能删除继承属性;

delete删除成功或删除不存在的属性或没有副作用时,返回true。

// 删除属性: 只能删除自有属性,不能删除继承属性
// 删除一个自身没有的属性也为true
	console.log(delete obj.name);//true
	console.log(obj.name);//undefined
	console.log(delete obj.toString);//true,但没删掉
	console.log(obj.toString());
	console.log(delete obj.age);//true
  • 修改属性
	// 修改属性: 给对象已有的属性赋值
			var prop="gender";
			obj[prop]="女";
  • 属性的查询和设置
//查询属性: . 和 []
  • 存取器属性getter和setter

属性值可以由一个或两个方法替代,这两个方法就是getter和setter

(a) 由getter和setter定义的属性,称为“存取器属性”;

(b) 一般的只有一个值的属性称为“数据属性”;

© 查询存取器属性的值,用getter;拥有getter则该属性可读;

(d) 设置存取器属性的值,用setter;拥有setter则该属性可写。

var obj = {
	name: "勋勋",
	birthday: "1994/04/12",
	bookName: "夏洛特烦恼",

	//可读属性
	get age() {
		return (new Date).getFullYear() -
			(new Date(this.birthday)).getFullYear();
	},

	//可写属性
	set age(value) {
		this.birthday = value;
	},
	get myBook() {
		return `<<${this.bookName}>>`;
	}
}

console.log(obj.age); //25

obj.age = "1998/10/01";
console.log(obj.age); //21
document.write(obj.myBook); //<<夏洛特烦恼>>

7.1.4 序列化对象

序列化对象是指将对象的状态转成字符串,也可以将字符串还原为对象;

(a) 转成字符串:JSON.stringify();

(b) 还原为对象:JSON.parse();

7.2 this机制

this是JavaScript语言中定义的众多关键字之一,它的特殊在于它自动定义于每一个函数域内。

7.2.1 this的误解

1. this引用function自身–误解

【this,一个特殊的对象】
1、全局作用域里的this: this-->window

2、函数里的this,全局下直接调用函数: this-->window

3、函数里的this,new 操作调用函数:this-->实例对象

4、函数里的this,函数写在对象的属性值里,通过对象属性调用函数:this-->该对象

//总而言之,谁调用的函数,函数里的this 就指向谁!!

5、函数里的this,使用call/apply 调用函数并传参,可以改变函数运行时this 的指向:this-->指定对象

2. this引用的是function的词法作用域–误解

 function fn1() {
     var a = 2;
     this.fn2();
 }
 function fn2() {
     console.log( this.a );
 }
 fn1(); //undefined

上面的例子并没有输出期望的数值2(以为this引用的是fn1的词法作用域),从而可以看到this并没有引用函数的词法作用域。

7.2.2 this运用规则

this到底绑定或者引用的是哪个对象环境决定于函数被调用的地方。

1. 默认绑定全局变量

当函数被单独定义和调用的时候,应用的规则就是绑定全局变量。如下:

 function fn() {
     console.log( this.a );
 }
 var a = 2;
 fn(); //2

2. 隐式绑定

 function fm() {
     console.log( this.a );
 }
 var obj = {
     a: 2,
     fn: fm
 };
 obj.fn(); //2
function fn() {
      console.log( this.a );
  }
  var obj2 = {
      a: 42,
      fn: fn
  };
  var obj1 = {
      a: 2,
     obj2: obj2
 };
 obj1.obj2.fn(); //42

3.显式绑定

bind()\apply()\call()函数,它接收的第一个参数即是上下文对象并将其赋给this。

// 函数里的this ,使用call/apply/bind 调用函数时,通过传入对象可以改变this 的指向
	var obj = {
		age: 28
	}

	function fn() {
		var age = 18;
		console.log(this.age);
	}
	new fn();

	console.log(fn.call(obj));//28
	console.log(fn.apply(obj));//28
	console.log(fn.bind(obj));

	fn();

	function identify() {
		return this.name.toUpperCase();
	}

	function sayHello() {
		var greeting = "Hello, I'm " + identify.call(this);
		console.log(greeting);
	}
	var person1 = {
		name: "Kyle"
	};
	var person2 = {
		name: "Reader"
	};
	identify.call(person1);
	identify.call(person2);
	sayHello.call(person1);//Hello, I'm KYLE
	sayHello.call(person2);//Hello, I'm READER

7.3 原型对象

1. 定义:
   原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。

2. 利用原型特点和概念,可以提取共有属性

3. 对象如何查看对象的构造函数-->constructor

4. 对象如何查看原型-->隐式属性__proto__

1. 函数的原型对象

在JavaScript中,我们创建一个函数A,那么浏览器就会在内存中创建一个对象B,而且每个函数都默认会有一个属性 prototype 指向了这个对象(即:prototype的属性的值是这个对象 )。

这个对象B就称作是函数A的原型对象,简称函数的原型。

这个原型对象B 默认会有一个属性constructor指向了这个函数A ( 意思就是说:constructor属性的值是函数A )。

<pre>
	1. 定义:原型是function对象的一个属性prototype,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
	2. 对象如何查看对象的构造函数-->constructor
	3. 对象如何查看原型-->隐式属性__proto__
	4. 利用原型特点和概念,可以提取共有属性
</pre>
<script type="text/javascript">
	console.log(  fn.prototype  )//函数的原型对象
	/*fn.prototype={
		constructor:fn
	}
	*/
	function fn(){
		/*var this={
			__proto__:fn.prototype
		}*/
		this.a=100;
		console.log(123);
		//return this;
	}
	var obj1=new fn;
	var obj2=new fn;
	console.log(obj1.__proto__==obj2.__proto__);//true
	console.log(obj1.__proto__==fn.prototype);//true
	console.log(obj1.constructor);//继承函数原型上的属性
	
	
	console.clear();
	CreateCat.prototype={
		type:"英短",
		sayHello:function(){
			console.log("喵~")
		}
	}
	//CreateCat.prototype.__proto__==Obejct.prototype
	//CreateCat.prototype{ constructor:CreateCat}
	function CreateCat(name,color,weight){
		/*
			var this={
				__proto__:CreateCat.prototype
			}
		*/
		this.name=name;
		this.color=color;
		this.weight=weight;
	}
	
	var cat1=new CreateCat("小明","yellow","4kg");
	var cat2=new CreateCat("小花","white","3kg");
	
	console.log(cat1.constructor,cat1.constructor==Object.prototype.constructor);
	// console.log( cat2.sayHello() );
	
	
</script>

2. 使用构造函数创建对象

当把一个函数作为构造函数 使用new创建对象的时候,那么这个对象就会存在一个默认的不可见的属性,来指向了构造函数的原型对象。这个不可见的属性我们一般用[[prototype]]来表示,只是这个属性没有办法直接访问到。

<body>
    <script type="text/javascript">
        function Person () {        
        }
        // 可以使用Person.prototype 直接访问到原型对象
        //给Person函数的原型对象中添加一个属性 name并且值是 "张三"
        Person.prototype.name = "张三";
        Person.prototype.age = 20;

        var p1 = new Person();
        /*
            访问p1对象的属性name,虽然在p1对象中我们并没有明确的添加属性name,但是
            p1的 [[prototype]] 属性指向的原型中有name属性,所以这个地方可以访问到属性name
            就值。
            注意:这个时候不能通过p1对象删除name属性,因为只能删除在p1中删除的对象。
        */
        alert(p1.name);  

        var p2 = new Person();
        alert(p2.name);  

        alert(p1.name === p2.name);  

        // 由于不能修改原型中的值,则这种方法就直接在p1中添加了一个新的属性name,然后在p1中无法再访问到
        //原型中的属性。
        p1.name = "李四";
        alert("p1:" + p1.name);
        // 由于p2中没有name属性,则对p2来说仍然是访问的原型中的属性。    
        alert("p2:" + p2.name);   
    </script>
</body>

原型练习:

	
	function CreateCar(color,weight){
		//var this={__proto__:CreateCar.prototype}
		this.color=color;
		this.weight=weight;
	}
	CreateCar.prototype={
		factory:"BMW",
		company:"德国"
	}
	
	var car1=new CreateCar("black","800kg");
	var car2=new CreateCar("white","850kg");

	car1.company="美国";
	console.log(car2.company);//"德国"
	
	car1.__proto__.factory="Saab";
	console.log(car2.factory);//Saab
	
	// CreateCar.prototype.company="日本"
	CreateCar.prototype={
		company:"日本"
	}
	CreateCar.prototype.company="中国";
	
	/* 
	如果通过car1/car2对象添加了一个属性company,则对对象来说就屏蔽了原型中的属性company。 
	换句话说:在car1中就没有办法访问到原型的属性company了。
	 */
	console.log(car1.company,car2.company)//"美国"  "德国" 
	

7.4 继承-原型链

ECMAScript需要通过原型链来实现继承。

完整原型链示意图:
在这里插入图片描述

7.4.1原型链

每一个对象都从原型继承属性,直到null结束。

所有的内置构造函数都有一个继承自Object.prototype的原型。

原型链的使用

function Grandpa(){
	this.lastname="勋勋";
	this.factory="exo";
}
Father.prototype=new Grandpa();
function Father(){
	this.fortune="一百万";
}
Son.prototype=new Father();
function Son(){
	this.name="小小";
}
var person=new Son();

/* 实例对象有一个__proto__属性,指向构造函数的原型对象 */
console.log(person.__proto__==Son.prototype);
console.log(Son.prototype.__proto__==Father.prototype);
console.log(Father.prototype.__proto__==Grandpa.prototype);

/* 构造函数有一个属性prototype,这个属性是一个对象,是Object的实例*/
console.log(Grandpa.prototype.__proto__==Object.prototype);
/* Object.prototype的原型__proto__->null; 
  形成链,到null结束 */
console.log(Object.prototype.__proto__==null);

总结:

一、构造函数、原型和实例的关系

  1.构造函数都有一个属性prototype,这个属性是一个对象,是Object的实例;
  2.原型对象prototype里有一个constructor属性,该属性指向原型 对象所属的构造函数;
  3.实例对象都有一个__proto__属性,该属性指向构造函数的原型对象;
  4.几乎所有对象, 最终原型为Object.prototype,原型链的终点为null.
	 obj.__proto__===Object.prototype
	  
二、prototype与_proto_的关系
	1.prototype是构造函数的属性;
	2.__proto__是实例对象的属性;
	3.两者都指向同一个对象;
	
// Array.prototype={
// 	constructor:Array
// }
var arr=[];//new Array
//对象的原型 对应为  它构造函数的原型

//几乎所有对象, 最终原型为Object.prototype,原型链的终点为null
console.log(arr.__proto__===Array.prototype)
console.log(Array.prototype.__proto__===Object.prototype)
console.log(Object.prototype.__proto__)//null

var myobj=Object.create(arr);
console.log(myobj.__proto__==arr)


var nul=Object.create(null);
console.log(typeof nul);
console.log(nul.__proto__);//undefined


var obj={};//new Object
console.log(obj.__proto__==Object.prototype)

// fn.prototype={
// 	constructor:fn
// }
var fn=function(){
	console.log(123)
};//new Function
console.log(fn.prototype.__proto__==Object.prototype);

console.log(fn.__proto__==Function.prototype);
console.log(Function.prototype.__proto__==Object.prototype)

console.log(Function.__proto__==Function.prototype)


7.4.2原型链上属性的增删改查

CreateCat.prototype={
	type:"英短",
	sayHello:function(){
		console.log("喵~")
	}
}
function CreateCat(name,color,weight){
	this.name=name;
	this.color=color;
	this.weight=weight;
}

var cat1=new CreateCat("小明","yellow","4kg");
var cat2=new CreateCat("小花","white","3kg");

//4\ 查, . 和 []   ,  检查 in可以检查继承来的属性
var prop="sayHello";
console.log( cat1[prop]  );
for(var x in cat1 ){	console.log(x)	};


//3\ 给对象自己没有的属性赋值,是增添自有属性;
cat1.type="中华田园猫";
console.log(cat1.type);//"中华田园猫"
console.log(cat2.type);//"英短"
console.log(delete cat1.type);//true
console.log(cat1.type);//"英短"

//2/ delete只能删除自己的属性,删除不掉继承属性,要通过__proto__访问才能删除原型上的属性
console.log(delete cat1.__proto__.type);//true,
console.log(cat1.type);

console.log(cat1.type);
//1/ 在对象上直接追加属性,是增添自有属性;要通过__proto__访问才能增添原型上的属性
cat1.__proto__.sayName=function(){
	console.log("my name is "+this.name)
}
console.log(cat2.sayName);


原型的使用

	// 通过增添对象原型上的属性和方法,追加通用属性和方法
	console.log(typeof document.querySelector("div"));
	document.querySelector("div").__proto__.editStyle
	=function(prop,value){
		this.style[prop]=value;
	}
	document.querySelector("div").editStyle("width","200px");
	document.querySelector("div").editStyle("height","200px");
	document.querySelector("div").editStyle("background","pink");
	
	document.querySelectorAll("div")[1].editStyle("width","200px");
	document.querySelectorAll("div")[1].editStyle("height","200px");
	document.querySelectorAll("div")[1].editStyle("background","yellow");
	

	var obj={};
	console.log(obj.toString());//[object Object]
	console.log(obj.__proto__==Object.prototype);
	console.log(obj.toString==Object.prototype.toString);
	
	var arr=[];
	console.log(arr.toString());//" " 空
	console.log(arr.__proto__==Array.prototype);
	console.log(arr.toString==Array.prototype.toString);
	
	console.log(delete Array.prototype.toString)//删掉了
	console.log(Array.prototype.toString());
	console.log(arr.__proto__.toString==Object.prototype.toString); //true
	console.log(arr.toString()); //[object Array]
	//把arr对象原型删掉了,变为最终原型object
	

8、数组

JavaScript 中的数组是一种特殊的对象,用来表示偏移量的索引是该对象的属性,索引只能是整数。

这些数字索引在内部被转换为字符串类型,因为 JavaScript 对象中 的属性名必须是字符串。

8.1 数组基础

8.11创建数组

创建数组的基本方式有两种。

(1) 第一种是使用Array构造函数:

var colors1 = new Array();		//定义空数组
var colors2 = new Array(20);	//定义长度为20的数组

(2) 第二种方式是使用数组字面量表示法:

var colors = ['red','blue','green'];
var names = [];

推荐使用 [] 操作符,和使用Array的构造函数相比,编写代码的速度更快,效率更高。

8.1.2 读写数组

8.1.2 读写数组

写–使用 [] 操作符将数据赋给数组:

var nums = []; 
nums[0] = 1;	//通过[]操作符赋值

for (var i = 0; i < 100; ++i) {  
    nums[i] = i+1;		
}

读–使用 [] 操作符读取数组中的元素:

var arr6 	= new Array({x:1, y:2}, h+5, h+9);
console.log(arr6[0]);

length属性反映的是当前数组中元素的个数,使用它,可以确保循环遍历了数组中的所有元素。

JavaScript 中 的数组也是对象,数组的长度可以任意增长,超出其创建时指定的长度。数组元素不连续的数组我们称为稀疏数组

var arr 		= new Array(5); 
arr[10]			= 6; 
arr[100] 		= 56; 	
console.log(arr);	
console.log(arr.length);	

8.1.3数组元素

  • 元素的添加:

    (a) 使用 [] 操作符;

    (b) arr.push(val),在数组末尾添加元素;

    © arr.unshift(val),在数组开头添加元素

    var arr = ['h', 'q', 'y', 'j'];
    arr[4] = 'five'; //['h', 'q', 'y', 'j',"five"]			
    arr.push('one');//['h', 'q', 'y', 'j',"five","one"]			
    arr.push('two_1', 'two_2');//['h', 'q', 'y', 'j',"five","one",'two_1', 'two_2']
    arr.unshift('first');//['first','h', 'q', 'y', 'j',"five","one",'two_1', 'two_2']		
    arr.unshift('first_1', 'first_2');	
    //区别:[]执行完毕,返回所添加的元素值;push(val)/unshift(val)执行完毕后返回数组长度
    
  • 元素的删除

    (a) delete arr[i]操作

    (b) arr.pop() 删除数组最后一个元素

    © arr.shift() 删除第一个元素

    var arr = ['h', 'q', 'y', 'j'];
    delete arr[2]; 	
    arr.pop();	
    arr.shift();	
    delete arr[0];
    //区别:
    //delete执行完毕返回true或false,只删除元素值不会改变数组长度;
    //pop()/shift()执行完毕返回删除的数组元素,改变数组长度
    
  • 遍历数组元素

    for循环对数组元素进行遍历:

    var numbers = [1,2,3,5,8,13,21]; 
    var sum = 0;
    for (var i = 0; i < numbers.length; ++i) {  
        sum += numbers[i];	//通过[]操作符读取
    }
    console.log(sum); 
    

8.1.4 数组复制

将一个数组赋给另外一个数组。

两种方式:浅复制和深复制。

  • 浅复制

    var nums = []; 
    for (var i = 0; i < 100; ++i) {    
        nums[i] = i+1;
    } 
    
    var samenums = nums;
    nums[0] = 400;
    console.log(samenums==nums)//true
    console.log(samenums[0]); 
    

    这种行为被称为浅复制,新数组依然指向原来的数组。

  • 深复制

    var nums = []; 
    for (var i = 0; i < 100; ++i) {  
       nums[i] = i+1; 
    } 
    
    var samenums=[];
    var length =nums.length;	//数组长度
    for(var i=0;i<length;i++){
        samenums[i]=nums[i]
    }
    console.log(samenums==nums)//false
    nums[0] = 55;
    console.log(samenums[0]);//1
    

8.2数组方法

JavaScript 提供了一组用来访问数组元素的函数,叫做存取函数。

8.2.1 查找/转换/拼接/截取

1.数组方法-查找与转换

(1) indexOf(val)

indexOf(val) 函数是最常用的存取函数之一,用来查找传进来的参数在目标数组中是否存在。如果目标数组包含该参数,就返回该元素在数组中的索引;如果不包含,就返回 -1。

	var arr = [123, 200, 345, true, [1], function() {}];
	//indexof(元素,start) 返回第一次查找到的位置,找不到返回-1
	console.log(arr.indexOf([1])); //-1

如果数组中包含多个相同的元素,indexOf()函数总是返回第一个与参数相同的元素的索引

(2) lastIndexOf(val)

lastIndexOf(val),该函数返回相同元素中最后一个元素的索引,如果没找到相同元素,则返回 -1。

数组与字符串转换

(1) 有两个方法可以将数组转化为字符串:join()和toString()。

这两个方法都返回一个包含数组所有元素的字符串,各元素之间用逗号分隔开。

	// .split(分隔符) 将字符串转换为数组
	var str1 = "2019/07/29";
	console.log(str1.split("/")); // ["2019", "07", "29"]
	var arr2 = str1.split("/"); 

	//.toString()  .join(自定义分隔符)
	// 将数组转成字符串
	var str2 = arr2.toString();
	console.log(str2);//2019,07,29
	console.log(arr2.join("-"));//2019-07-29

(2) 调用字符串对象的split(‘分隔符’) 方法也可以生成数组。

该方法通过一些常见的分隔符,比如分隔单词的空格,将一个字符串分成几部分,并将每部分作为一个元素保存于一个新建的数组中。

var sentence = "the quick brown fox jumped over the lazy dog"; 
var words = sentence.split(" "); 		//传参空格,通过空格将字符串分离成数组[]
for (var i = 0; i < words.length; ++i) {   
     console.log("word " + i + ": " + words[i]);
}

2.数组方法-拼接与截取

concat() 和 splice() 方法允许通过已有数组创建新数组。

(1) arr1.concat(arr2,arr2,val1,…) 方法可以合并多个数组,创建一个新数组。

该方法的发起者是一个数组,参数是另一个数组。作为参数的数组,其中的所有元素都被连接到调用concat()方法的数组后面。

(2) arr.splice(index,length) 方法截取一个数组的子集创建一个新数组。

	var arr1 = [1, 2, 3],
		arr2 = ["a", "b", "c"],
		arr3;

	/* 1.拼接 
	.contact(数组),产生新数组,对原数组的值不产生影响 */
	console.log(arr1.concat(arr2)); //[1,2,3,"a","b","c"]
	console.log(arr1, arr2); //[1,2,3]  ["a","b","c"]
	console.log(arr3 = arr2.concat(arr1)); //["a","b","c",1,2,3]

	/* 2.截取 
	.splice(start,length)对原数组产生影响
	.slice(start,end)对原数组不产生影响 */
	console.log(arr3.slice(1, 3)); //["b","c"]
	console.log(arr3); //["a","b","c",1,2,3]

	console.log(arr3.splice(1, 8)); //["b","c",1,2,3]
	console.log(arr3); //["a"]

8.2.2增删函数

  • 数组元素的增删:

    – push():在数组的结尾添加一个或多个元素,返回新数组的长度;

    – pop():移除数组最后一个元素,返回移除的元素;

    – unshift():在数组的开头添加一个或多个元素,返回新数组的长度;

    – shift():删除数组第一个元素,并返回删除的元素;

    –这四个函数都是在原始数组上进行修改;

  • splice() 从数组中间添加、替换 和删除元素,splice(startindex,length,val1,val2,val3……)

/* 增添元素:
	length = 0,相当于给数组增添元素 
	*/
  var arr = [1, 2, 3, 100, 200, 300, 4, 5]; 
	console.log(arr.splice(0, 0, 2, 3, 4));
	console.log(arr); //[2,3,4,1,4,5]

// 替换元素
	// length与参数[v1,v2,v3..]的长度一致相当于替换
	console.log(arr.splice(0, 3, 200, 300, 400), arr); 
	//[2,3,4] [200,300,400,1,4,5]


/* 删除元素:
	splice(start,length,v1,v2,v3...)
	先进行字符串截取,再从start[v1,v2,v3..]插入数组 
	*/
	
	console.log(arr.splice(1, 5)); //[2,3,100,200,300]
	console.log(arr); // [1,4,5]

(1) 添加:arr1.splice(startIndex,0,arr2),注第二个参数为0:往前边前边添加

(2)arr1.splice(startIndex,length,arr2),注第二个参数为被替换的数组长度

(3) 使用 splice(startIndex,length) 方法从数组中删除元素。

8.2.3 排序函数

  • reverse(),将数组中元素的顺序进行翻转。
//1.  .reverse()
	var arr = [1, 200, 300, 3, 5];
	console.log(arr.reverse());
	//[5, 3, 300, 200, 1]
  • sort(),对字符串进行排序,字母按顺序排序(大写字母均比小写字母小)。
// 2.  .sort()默认按照字符编码排序
	var arr2 = [5, 3, 2, 4, 1, 105, 205];
	console.log(arr2.sort());
	// [1, 105, 2, 205, 3, 4, 5]

sort方法接受compareFunction作为参数,然后sort会用它排序数组,使数组按升序排序。

	//升序排列
	function compare(a, b) {
		return a - b; //升序
		// return b-a; //降序
		/* if (a < b) {
			return -1;
		}
		if (a > b) {
			return 1;
		}
		// a必须等于b
		return 0; */
	}
	console.log(arr2.sort(compare));
	//[1, 2, 3, 4, 5, 105, 205]
	//[205, 105, 5, 4, 3, 2, 1]

	//降序
	var arrObj = [
        {name: '花花',grade: 88}, 
        {name: '灿灿',grade: 99}, 
        {name: '开开',grade: 66}, 
        {name: '勋勋',grade: 100}, 
        {name: '晨晨',grade: 77}
    ];
	function myCompare(prop, bol) {
		function compare(a, b) {
			if (bol) {
				return a[prop] - b[prop];
			} else {
				return b[prop] - a[prop];
			}
		}
		return compare;
	}
	console.log(arrObj.sort(myCompare("grade", false)));
	/*
	0: {name: "勋勋", grade: 100}
	1: {name: "灿灿", grade: 99}
	2: {name: "花花", grade: 88}
	3: {name: "晨晨", grade: 77}
	4: {name: "开开", grade: 66} 
	*/

要得到字符串忽略大小写的比较数组,自定义方法:

	function myStrCom(a, b) {
		console.log(a, b);
		/* 
		apple World 
		cheers apple
		cheers World
		cheers apple
		*/
		if (a.toUpperCase() > b.toUpperCase()) {
			return 1;
		} else {
			return -1;
		}
		return 0;
	}
	var strArr = ["apple","cheers","World"];
	console.log(strArr.sort());
	// ["World", "apple", "cheers"]
	console.log(strArr.sort(myStrCom));
	// ["apple", "cheers", "World"]

8.2.4 迭代器方法

这些方法的参数都是函数,可以对数组中的每一个元素运用该函数,原数组不变。

1、不生成新数组 的迭代器方法

(1) forEach() :对数组中的每一项运行给定函数,没有返回值,它和使用for循环的结果相同

//1.  forEach() 该方法返回值是undefined
	var arr=[1,2,,4],sum=0;
	var forEachBack=arr.forEach(function(item,index,arr){
		console.log(item,index,arr);
		//[1, 2, empty, 4]
		sum+=item;
	})
	console.log(sum,forEachBack);//5 undefined
	
	// 比for效率高
	for(var i=0;i<arr.length;i++){
		console.log(arr[i]);
	}
//item 指代元素值,index 指代元素索引,array 指代数组本身,形参位置固定

(2) every():对数组中的每一项运行给定函数,如果该函数对每一个项都返回true,则返回true;

/*
	 2. every(function fn(item,index,arr){}),返回值一定为Boolean
	若每一项迭代结果都为true,every返回值才为true;
	若有一项为false就返回false(默认返回false)。 
	*/
	var everyBack=arr.every(function(item){
		console.log(item);
		return item%2==0;
	});
	console.log(everyBack);//false

//也可参照上面匿名函数的例子,给every接收的函数传参(item,index,array)

(3) some():对数组中的每一项运行给定函数,如果该函数对任一一项返回true,则返回true(默认返回false)。

	/*
	 3. some(function fn(item,index,arr){})
	返回值一定为Boolean
	将每一项迭代结果进行逻辑或,记住短路规则 
	*/
	var someBack=arr.some(function(item){
		return item%2==0;
	})
	console.log(someBack);//true

//也可参照匿名函数的例子,给every函数传参(item,index,array)

2、生成新数组 的迭代器方法

(1) map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。

	// 1. map(function fn(item,index,arr){})
	// 将每一次迭代的返回值放到新数组
	var arr = [1, 2, 3, 4];
	var newArr = arr.map(function(item, index, arr) {
		return item * item;
	});
	console.log(arr, newArr);
	// [1, 2, 3, 4]   [1, 4, 9, 16]

(2) filter():传入一个返回值为布尔类型的函数。

和every()方法不同的是, 当对数组中的所有元素应用该函数,结果均为 true 时,该方法并不返回true,而是返回一个新数组,该数组包含应用该函数后结果为 true 的元素。

	// 2.  filter(function fn(item,index,arr){})
	// 将返回值为true的item放到新数组里面去
	var person = [
        {name: '花花',grade: 88}, 
        {name: '灿灿',grade: 99}, 
        {name: '开开',grade: 66}, 
        {name: '勋勋',grade: 100}, 
        {name: '晨晨',grade: 77}
        ]
	var newPerson=person.filter(function(item,index,arr){
		return item.grade>=88;
	}).length;
	console.log(newPerson);// 3

8.2.5 累加方法

reduce和reduceRight:使用指定的函数对数组元素进行组合,生成一个值,接收两个参数。

详细解释:

– 参数一:要执行的函数,有返回值,函数内部处理的参数如下

  • previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
  • currentValue (数组中当前被处理的元素)
  • index (当前元素在数组中的索引)
  • array (调用 reduce 的数组)

– 参数二:传递给函数的默认值,可忽略

– reduce从左向右操作,reduceRight表示从右向左操作。

	// reduce(function(pre,item,index,arr){return},initialValue)
	
	// 求累加:
	var a=[1,2,23,24,5,6,45,154,687,800];
	var sum=a.reduce(function(a,b,c,d){
		console.log(a,b,c,d);
		return a+b;
	} , 0 );
	
	//累积:
	var ji=a.reduce(function(a,b,c,d){
		return a*b;
	} , 1 );
	console.log(ji);//126145071360000
	
	//求数组最大值:
	var maxV=a.reduce(function(a,b,c,d){
		return a>b?a:b;
	})
	console.log(maxV);//800

8.2.6 类型判断

	var arr=[];
	// arr instanceof Array
	console.log(Array.isArray(arr));//true
	
	var obj={};
	console.log(Array.isArray(obj));//false
	
	// 类数组对象:键名是从0开始自然数迭增,length属性标明属性个数,是一个对象
	var leiObj={
		0:"abc",
		length:1
	}
	console.log(typeof leiObj); //object
	console.log(Array.isArray(leiObj));//false
	
	// arguments:类数组对象。只能写在函数体内
	// 每一项值表示函数接收到的实参
	function fn1(){
		for(var i=0;i<arguments.length;i++){
			console.log(arguments[i]);
		}
		console.log(arguments.length);//3
		console.log(typeof arguments); //object
		console.log(Array.isArray());//false
	}
	fn1('a','b','c');

8.3 类数组

类数组对象:

只包含使用从零开始,且自然递增的整数做键名,并且定义了length表示元素个数的对象,我们就认为他是类数组对象!

arguments类数组对象:

//类数组
function fn () {
		console.log(arguments);//Arguments(4)
		console.log(typeof arguments);//object
		console.log(Array.isArray(arguments));//false
}
	// js不允许函数重构,默认调用最后一个函数体
	function myfn1(a){
		console.log(a);
	}
	function myfn1(a,b){
		console.log(a+b);
	}
	function myfn1(a,b,c){
		console.log(a+b+c);
	}
	myfn1(100,200,3);
	
	
	//传递几个参数就累加几个
	var myfn=function(){
		var sum=0;
		for(var i=0;i<arguments.length;i++){
			sum+=arguments[i];
		}
		return sum;
	}
	console.log(myfn(2,5,80));

arguments最重要的用法–函数重载

//传递的实参与形参对应时,形参和实参存在一定的映射
function myfn(a,b){
	console.log(a,b);
	console.log(arguments[0],arguments[1]);
	arguments[0]="abc";
	console.log(a);
	b=200;
	console.log(arguments[1]);//undefined
}

myfn(100);

8.4 二维和多维数组

JavaScript 只支持一维数组,但是通过在数组里保存数组元素的方式,可以轻松创建多维数组。

  • 创建二维数组

    二维数组类似一种由行和列构成的数据表格。在 JavaScript 中创建二维数组,需要先创建一个数组,然后让数组的每个元素也是一个数组。

    [[],[]]

    最起码,我们需要知道二维数组要包含多少行,有了这个信息,就可以创建一个 n 行 1列的二维数组了:

    var twod = [];
    var rows = 5;
    for (var i = 0; i < rows; ++i) {
    	twod[i] = [5];
    }
    [
    	[1, 2, 3, 4, 5]
    ]
    [
    	[5],
    	[5],
    	[5],
    	[5],
    	[5]
    ] //最外层数组的数据项对应的是行,每一个数据项里有几个元素对应该行就有几列
    
  • 处理二维数组

    处理二维数组中的元素,有两种最基本的方式:按列访问和按行访问。

    var grades = [
    	[89, 77, 78],
    	[76, 82, 81],
    	[91, 94, 89]
    ];
    var total = 0;
    var average = 0.0;
    for (var row = 0; row < grades.length; ++row) {
    	for (var col = 0; col < grades[row].length; ++col) {
    		total += grades[row][col];
    	}
    	average = total / grades[row].length;
    	console.log("Student " + parseInt(row + 1) + " average: " +
    		average.toFixed(2));
    	total = 0;
    	average = 0.0;
    }
    /* 
    Student 1 average: 81.33
    Student 2 average: 79.67
    Student 3 average: 91.33
     */
    
  • 参差不齐的数组

    参差不齐的数组是指数组中每行的元素个数彼此不同。有一行可能包含三个元素,另一行可能包含五个元素,有些行甚至只包含一个元素。

    var grades = [
    	[89, 77],
    	[76, 82, 81],
    	[91, 94, 89, 99]
    ];
    var total = 0;
    var average = 0.0;
    for (var row = 0; row < grades.length; ++row) {
    	for (var col = 0; col < grades[row].length; ++col) {
    		total += grades[row][col];
    	}
    	average = total / grades[row].length;
    	console.log("Student " + parseInt(row + 1) + " average: " + average.toFixed(2));
    	total = 0;
    	average = 0.0;
    }
    /* 
    Student 1 average: 83.00
    Student 2 average: 79.67
    Student 3 average: 93.25
    */
    

多维数组,外层数组的每一项也是一个数组;
外层数组看作行,内层数组看作列。

9.函数

通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。

函数即对象,程序可以随意操控它们。

函数可以嵌套在其他函数中定义,这样它们就可以访问它们被定义时所处的作用域中的任何变量。

9.1 函数概述

一处定义,处处调用;

– 如果把函数作为一个对象的属性,则称为方法;

– 每次调用函数会产生一个this:谁调用这个函数或者方法,this就指向谁;

– 函数就是对象,可以给他设置属性或方法;

9.1.1 函数定义

总共有三种函数定义的方式:函数声明语句、函数表达式、内置构造函数。

函数声明语句:

function functionName(parameters) {
  	//执行的代码
}
//计算两个坐标点之间的距离
function distance (x1, y1, x2, y2) {
    var x = x2 - x1;
    var y = y2 - y1;
    return Math.sqrt(x*x + y*y).toFixed(2);
}
console.log(distance(3, 5, 8, 20));

小练习:定义一个求阶乘的函数。

// 1.函数声明表达式,有函数提升
function jiecheng(n){
	/* varji=1;
	for(n;n>0;n--){
		ji*=n;
	}
	return ji; */
	if(n==1)  return 1;
	if(n!=1)  return n*jiecheng(--n);
	if(n==0)  return 1; //0!=1
}
console.log(jiecheng(5));

// 2.函数定义表达式,无函数提升
var myfn1=function(n){
	if(n==1)  return 1;
	if(n!=1)  return n*myfn1(--n);
	if(n==0)  return 1;
}
console.log(myfn1(5));//函数定义表

  • 函数表达式
var functionName  = function (parameters) {
  	//执行的代码
};
//函数以分号结尾,因为它实际上是一个执行语句

定义一个表达式函数:

var square = function factorial(h) {
    if(h===1) {return 1;}
  	else     {return h*factorial(h-1);}
}
console.log(square(3));
  • Function内置构造函数

在以上实例中,我们了解到函数通过关键字 function 定义。

函数同样可以通过内置的 JavaScript 函数构造器(Function())定义。

用Function()构造函数创建一个函数时并不遵循典型的作用域,它一直把它当作是顶级函数来执行。

var y = "global";
function constructFunction() {
  var y = "local";
  function test(){};
  return new Function("return y"); // 无法获取局部变量,作用域始终是全局作用域 
}
alert(constructFunction()()); 

函数可以嵌套在其他函数里面,也就是在函数里面可以定义函数。

被嵌套的函数可以访问嵌套他们的函数的变量或参数。

9.1.2 函数调用

javascript一共有4种调用模式:

函数调用模式、方法调用模式、构造器调用模式 和 间接调用模式。

每种方式的不同在于 this 的初始化。

  • 1.函数调用模式
var myfunc = function(a,b){
  	console.log(this);
  	return a+b;
}
alert(myfunc(3,4)); //window.myfunc

函数调用模式–结论:

a, this是指向Window的

b, 返回值是由return语句决定的,如果没有return则表示没有返回值

  • 2.方法调用模式 ( 括号调用 )

先定义一个对象,然后在对象的属性中定义方法,通过myobject.property来执行方法。

var name = "james";
var obj = {
  name : "wade",
  fn1 : function () {
    console.log(this.name);
  }  
};
obj.fn1(); 

方法调用模式 ( 括号调用 )–结论:

a, this 是指向调用该方法的对象

b, 返回值还是由函数内部return语句决定,如果没有return表示没有返回值

  • 3.构造器调用模式 ( new 调用 )

如果函数或者方法调用之前带有关键字new,它就当成构造函数调用。

function Fn () {
    //var this={__proto__:Fn.prototype}
 	this.name = "james";
 	this.age = 32;
 	console.log(this);   
    //return this;
};
var fn1 = new Fn();  
console.log(fn1);	  

构造器调用模式 ( new 调用 )–结论:

a, this是指向构造函数的实例化对象,返回值一定是引用类型

b, 如果没有添加返回值的话,默认的返回值是this

c. 添加返回值若为原始类型, 依旧返回this;

d. 添加返回值若为引用类型, 返回值为手动返回的引用值;

  • 4.间接调用模式

也称之为“apply、call调用模式” 或 “上下文调用模式”。

/* 
3. call方法 / apply方法 / bind方法, this指向--第一个参数(对象) 
   返回值由return决定
		*/
var myobject = {};
var sum = function(a, b) {
	console.log(this);
	return a + b;
};

/*
 call()接收多个参数,第一个参数(对象),其余参数传递给函数的实参
 apply()只接收两个参数,第一个参数(对象),第二个参数(数组/类数组(数组元素对应为实参)) 
*/
var sum2 = sum.call(myobject, 10, 30);
console.log(sum2);//40

下面函数调用中this指向如何?

function f1() {
	console.log(this);
}
f1.call(null); //window			
f1.call(undefined); //window
f1.call(123); //number   
f1.call("abc"); //string  
f1.call(true); //boolean  
f1.call([1, 2, 3]); //array(3)

间接调用模式–结论:

a, 传递的参数不同,this的指向不同,this会指向传入参数的数据类型

b, 返回值是由return决定,如果没有return表示没有返回值。

补充1:函数提升

提升(Hoisting)是 JavaScript 默认将当前作用域提升到前面去的的行为;

提升应用在变量的声明与函数的声明。

myFunction(5);

function myFunction(y) {
	return y * y;//25
}

使用表达式定义函数时无法提升

小练习:以下js 执行完毕之后打印结果?

该题运用到js 函数重载与函数提升知识,js函数不允许重载(即同名函数只能有一种定义)

function f() {
	return 1;
}
console.log(f()); //4

var f = new Function("return 2;");
console.log(f()); //2

var f = function() {
	return 3;
}
console.log(f()); //3

function f() {
	return 4;
}
console.log(f()); //3

var f = new Function("return 5;");
console.log(f()); //5

var f = function() {
	return 6;
}
console.log(f()); //6

补充2:自调用函数

(1)自调用表达式。 函数表达式可以 “自调用”。

函数自调用完成,函数自动被销毁。

如果表达式后面紧跟 () ,则会自动调用:

// 自调用函数也可称之为”立即执行函数“,函数执行完函数空间就被释放,不能再被访问,但函数返回值可以被保存。
var fn0 = function() {
	console.log(123);
	var x = 100;
	return x;
}();

var fn1 = function(x, y) {
	console.log(x, y);
	return x + y;
}(3, 4);
console.log(fn1); //7

//强制运算符()
//方式一,调用函数,得到返回值。强制运算符使函数调用执行
var fn3 = (function(x, y) {
	console.log(x, y);
	return x + y;
}(3, 4));
console.log(fn3); //7

//方式二,调用函数,得到返回值。强制函数直接量执行再返回一个引用,引用在去调用执行
var fn2 = (function(x, y) {
	console.log(x, y);
	return x + y;
})(3, 4);
console.log(fn2); //7

(2) 不能自调用声明的函数!

通过添加括号,来说明它是一个函数表达式:

(function funcName() {
			var x = "Hello!!";
			console.log(x);			
}());
//1.整体声明上加括号

(function funcName() {
    var x = "Hello!!";
    console.log(x);			
})();		
//2.function定义部分加括号,不能写成function(){}()这种格式。

//3.省略括号, 加上void, 即忽略返回值
//让 JavaScript 引擎把一个function关键字识别成函数表达式而不是函数声明
void function funcName() {
    var x = "Hello!!";
    console.log(x);			
}();

语法总结:只有函数表达式才能被执行符号()执行。

+ function test(){
    console.log("a")
}();
//正常打印出a,因为正号+将函数声明转化成了函数表达式
//还可以用负号-、叹号!等 ( * 和 / 号不可以)
//其实上面“函数表达式”自调用的写法就是通过等号=将函数声明转化成了函数表达式
//总之,匿名函数自动执行,多种写法,只要不要function开头,开头加上不报错的符号就行

练习一下:

function test(a,b,c,d){
	console.log(a+b+c+d);
}
(1234//浏览器报错么?为什么。

//不报错,js引擎解析时时尽量不报错就不报错的,除非错得不能再错
//在上面的代码中,js引擎时将函数声明function test(){…………}和逗号运算符(1,2,3,4)分开来看的,所以js引擎不报错但函数也不会被调用执行,不会有打印信息的。
//解释:逗号运算符,即返回逗号语句最后一个表达式的值。

再小小练习一下:

var f = (
	function f() {
		return "1";
	},
	function g() {
		return 2;
	}
)();
console.log(typeof f); //"number"

补充3:函数名后的多个括号

f()意思是执行f函数,返回子函数

f()()执行子函数,返回孙函数

f()()()执行孙函数

//"函数调用"这个表达式的值始终由函数返回值决定

f的函数体里要return 子函数,子函数里要return 孙函数,如果没有return关键字,不能这样连续执行的。

//写一个加法函数,完成 5+6

function myadd(a) {
	function myadd1(b) {
		return a + b;
	}
	return myadd1;
}
console.log(myadd(5)(6)); //11

9.2 函数参数

javascript函数的参数与大多数其他语言的函数的参数有所不同。

函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型,甚至可以不传参数。

特殊情况1:同名形参

在非严格模式下,函数中可以出现同名形参,且只能访问最后出现的该名称的形参。

function add(x,x,x){
    return x;
}
console.log(add(1,2,3));//3

在严格模式下,出现同名形参会抛出语法错误。

特殊情况2:参数个数

当实参比函数声明指定的形参个数要少,剩下的形参都将设置为undefined值。当实参多于形参,则只使用有效的实参,多出部分没影响。

function add(x,y){
    console.log(x,y);//1 undefined
}
add(1);

9.2.1 参数分类

函数参数分为两类:函数显式参数(Parameters)与隐式参数(Arguments)

1.显式参数(Parameters)

函数显式参数在函数定义时列出(即形参)。

函数调用未传参时,参数会默认设置为: undefined。有时这是可以接受的,但是建议最好为参数设置一个默认值:

	// js函数参数:函数调用-传递的实参不受限制,个数/类型
	// 分类:显式参数(形参)/隐式参数(arguments)
	// 显式参数(形参),同名形参,只有最后一个有效;默认值为undefined

	function fn(x){
		console.log(x);//传入第一个参数
		console.log(arguments);//参数全部传入
	}
	fn(1,3,40,5,6); //函数调用传参不受限制

2.隐式参数(Arguments)

JavaScript 函数有个内置的对象 arguments 对象。

argument 对象包含了函数调用的参数数组(实参数组)。

// 隐式参数(arguments)
var x = findMax(1, 123, 500, 115, 44, 88);
//js 重构
function findMax() {
	var i, max = arguments[0];
	if(arguments.length < 2) return max;
	for (i = 0; i < arguments.length; i++) {
		// if (arguments[i] > max) {
		// 	max = arguments[i];
		// }
		max=arguments[i]>max?arguments[i]:max
	}
	return max;
}
console.log(x);//500

arguments对象与传入参数的映射规则

//形参实参映射规则
function sum(a,b){
	arguments[1]=4;
	
	console.log(arguments[1]);
	console.log(b);
}
sum(1); //4 undefined
sum(1,2);//4  4

//arguments对象与形参是相互独立的,但又存在映射规则:
//当传入参数与形参个数相等时,arguments对象与形参才是一一对应的;
//当传入参数与形参个数不等时,arguments对象与有传入参数的形参才存在映射规则。

练习一道阿里巴巴2013年的一道笔试题:

下面代码中console.log的结果是[1,2,3,4,5]的选项是(ACD//A
function foo(x){
	console.log(arguments)
	return x;
}
foo(1,2,3,4,5)

//B
function foo(x){
	console.log(arguments)
	return x;
}(1,2,3,4,5)
//函数不能自调用,也没有括号强制调用  function(){}()不可自调用

//C
(function foo(x){
	console.log(arguments)
	return x;
})(1,2,3,4,5)

//D
function foo(){
	bar.apply(null,arguments);{0:1,1:2}
}
function bar(x){
	console.log(arguments);{0:{0:1,1:2}}
}
foo(1,2,3,4,5)

9.3 js的预编译

js完成解释执行分为三个步骤:

1.语法分析;

2.预编译(全局预编译、函数预编译);

3.执行语句。

9.3.1 函数预编译

举个例子瞧一瞧:

{
    a:123,
    b:function() {},
    d:function d() {}
}

function fn(a) {
	console.log(a);//function a() {}
	var a=123;
	console.log(a);//123
	function a() {}
	console.log(a);//123
	var b = function() {}
	console.log(b);//function() {}
	function d() {}
}
fn(1);1)变量和形参的提升至AO对象,赋值undefined
AO{
    a:undefined,
    b:undefined,
    d:undefined
}2)找实参的值赋给形参
AO{
    a:1,
    b:undefined,
    d:undefined
}3)函数声明提升
AO{
    a:function a() {},
    b:undefined,
    d:function d() {}
}

  1. 语法分析

    符号、大括号等语法检查;

  2. 函数预编译

    变量声明提升,function函数声明整体提升;发生在函数执行的前一刻(实际过程如下):

    (1) 创建AO对象--Activation Object(执行期上下文):AO{ }
    (2) 找形参和变量声明,将变量和形参名作为AO属性名,即变量提升过程,值为undefined;
    (3) 将实参的值放到形参中去
    (4) 在函数体里面找函数声明,值赋予函数体
    
    (1) 创建AO对象--Activation Object(执行期上下文):
    AO{ }
    
    (2) 找形参和变量声明,将变量和形参名作为AO属性名,即变量提升过程,值为undefined;
    AO {
    	a:undefined,
    	b:undefined
    }
    
    (3)将实参的值放到形参中去
    AO {
    	a:1,
    	b:undefined
    }
    
    (4)在函数体里面找函数声明,值赋予函数体
    (a属性在AO对象中已存在,故只增加d属性):
    AO {
    	a:1,
    	b:undefined,
    	d:
    }
    (给属性值赋予函数体):
    AO{
    	a:function a() {},
    	b:undefined,
    	d:function d() {}
    }
    
  3. 解释执行

    再看一眼函数:

function fn(a) {
	console.log(a);
	var a=123;
	console.log(a);
	function a() {}
	console.log(a);
	var b = function() {}
	console.log(b);
	function d() {}
}
fn(1);

函数执行:

1. 打印:function a() {},
2. 变量赋值(变量a声明已在预编译阶段完成)
  AO{
	a:123,
	b:undefined,
	d:function d() {}	
}
3. 打印:123,
4. 打印:123(打印语句的上一行,函数a声明的提升已在预编译阶段完成),
5. 变量赋值(变量b提升已完成)
  AO{
	a:123,
	b:function() {},
	d:function d() {}
}
6. 打印:function() {},
7. 函数执行完成(函数d声明的提升已在预编译阶段完成)

打印输出:

function a() {}
123
123
function() {}

9.3.2 全局预编译

全局“即从页内js的script 的开始标签到结束标签,从页外js文件的第一行到最后一行。

1、生成一个GO对象–Global Object{},GO===window
2、变量提升
3、函数提升

例子:

console.log(a)//function a(){}
var a=123;
function a(){}
console.log(a)//123

GO:{
    a:123
}

该例子的预编译过程:

1GO{}
2GO{
    a:function a(){}
}
3、执行
   a. 打印:function a(){}
   b. GO{a:123}
   c. 打印:123

练习一:

function test(){
	var a=b=123;
}
test();

//函数优先在自己的AO 内查找变量,找不到逐层向外查找;
//最外层GO==window依然找不到该变量,但要赋值时,在全局上GO==window创建该变量并赋值
GO{
    test:function(){},
    b:123
}
AO{
    a:123
}

练习二:

console.log(test);//function……
function test(test) {
	console.log(test);//function test() {}
	var test=234;
	console.log(test);//234
	function test() {}
}
test(1);
var test=123;

GO{
	test:function test(test) {
				console.log(test);
				var test=234;
				console.log(test);
				function test() {}
			}
}

AO:test {
	test:234
}

练习三:

//函数每调用一次,都要重新做一次函数预编译;函数调用完成,其执行期上下文不可再访问,被"销毁"
var x=1,y=z=0;
function add(n){
	return n=n+1;
}
y=add(x);
function add(n){
	return n=n+3;
}
z=add(x);

console.log(x,y,z)//1  4  4

例子解析:

var global=100;
function fn() {
	console.log(global);
}
fn();

过程分析:

1.全局预编译
GO{
	global:undefined,
	fn:function fn() {
			console.log(global);
		}
}
2.执行语句global=100
GO{
	global:100,
	fn:function fn() {
			console.log(global);
		}
}
3.执行语句fn(),发生函数预编译(语句上面的函数fn声明已提升)
	(1)函数预编译
	AO{
		
	}
	(2)执行就函数语句,AO对象中没有global,故在GO中找global(如果AO中有global则在AO中找)
	打印:100

练习:

global = 100;
function fn() {
	console.log(global);
	global=200;
	console.log(global);
	var global =300;
}
fn();
var global;

GO{
    global:100,
    fn:function fn() {
        console.log(global);//undefined
        global=200;
        console.log(global);//200
        var global =300;
}
}

AO{
    global:300
}

练习:

function test() {
	console.log(b);//undefiened
	if(a) {
		var b=100;
	}
	console.log(b);//undefined
	c=234;
	console.log(c);//234
}	
var a;
test();
a=10;
console.log(c);//234

GO{
    a:10,
    test:函数体function test() {
	console.log(b);
	if(a) {    ,
    c:234
}

        AO{
            b:undefined
        }

练习:

function bar() {
	return foo;
	foo=10;
	function foo() {	}
	var foo=11;
}
console.log(bar();//   function foo() {	}
GO{bar:bar*******}
AO{
    foo:function foo() {	}
}



console.log(bar());//11
function bar() {
	foo=10;
	function foo() {	}
	var foo=11;
	return foo;
}
GO{bar:bar*******}
AO{
    foo:11
}

9.3.3 作用域链

执行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。

1.当函数被定义,其作用域()集合就存储它诞生环境下的执行期上下文GO

2.当函数被调用,进行函数预编译,生成一个自己的执行期上下文AO,放置到函数作用域链头部

3.当函数调用完毕,作用域链头部的执行期上下文被销毁(shift)

 
作用域链是什么?
    答:js作用域链是函数的属性,不可手动访问,JS引擎才能访问;
    该属性存储着函数的执行期上下文对象的集合。这个集合呈链式连接,我们把这种链式连接叫做作用域链。 

特点:它是函数执行的环境;
		函数被定义继承所在环境的作用域链;
		函数每次被调用进行一次函数预编译,产生一个新的执行期上下文,放置到自己的作用域链头部;
		函数执行完毕后,作用域链头部的执行期上下文被销毁。
		一个函数只要它还存在,还能被访问到,它的作用域链就还存在。
  • [[scope]]:每个js函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供js引擎存取,[[scope]]就是其中一个。

    function test() {	}
    
    我们可以访问的函数属性(如:test.length/test.prototype);
    我们不能访问但着实存在的函数属性(如:test.[[scope]])
    

    [[scope]]指的是我们所说的作用域,其中存储了运行期上下文的集合。

  • 作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式连接,我们把这种链式连接叫做作用域链。

/* 只要函数存在,它的作用域链就还存在
函数预编译生成自己的执行期上下文(AO),执行语句沿着作用域链头部依次往下访问 
*/

function a() {
function b() {
function c() {

}
c();
}
b();
}
a(); 

/* 
AO:a{
	b:function b() {}
}
AO:b{
	c:function c() {}
}
GO{
	a:function a() {}

} */

9.4 函数闭包

闭包:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

面试题:闭包是什么?
答:闭包是可访问上一层函数作用域里变量的函数,即便上一层函数已经关闭。

/* 
 函数闭包:返回的子函数可以访问上一级函数的局部变量
 */
function a() {
	var num=100;
	function b() {
		num++;
		console.log(num);//101  102
	}
	return b;
}
var demo=a();
demo();
demo();


/* b(demo)函数作用域链
<!-- AO(b){
	
} -->

AO(a){
	num:102,
	b:function b() {……
}

GO{
	demo:function b() {……,
	a:function a() {……
}

<!-- a函数作用域链 -->

GO{
	demo:function b() {……,
	a:function a() {……
} */

9.4.1 闭包的特点

  1. 作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
  2. 一个闭包就是当一个函数返回时,一个没有释放资源的栈区。

简单的说,Javascript允许使用内部函数—即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。

优点:可以访问局部变量。

缺点:局部变量一直占用内存,内存占用严重,还容易造成内存泄漏(内存被占用,剩余的内存变少,程序加载、处理速度变慢).

/* 闭包:返回的子函数能够访问外部函数的变量和方法,就是一个闭包
 	 优点--能够通过闭包访问操作局部变量
 	 缺点--局部变量一直占用内存,容易造成内存泄漏(只要函数还存在它就有作用域链,函数作用域链上始终保存着外部函数的执行期上下文)
*/

9.4.2 闭包的几种写法

闭包的写法汇总:

1.写在原型对象的方法上

2.内部函数语句访问外部函数的变量,将内部函数写在外部函数的return中

3.通过表达式写在对象的方法上

4.通过属性创建写在对象的方法上

5.通过全局变量赋值(类似于第二种写法的原理)

第一种:写在原型对象的方法上

//第1种写法  
function Person() {
}
Person.prototype.type="人类";
Person.prototype.getType=function () {
    return this.type;
}
var person = new Person();
console.log(person.getType());//"人类"

第二种:内部函数语句访问外部函数的变量,将内部函数写在外部函数的return中

	var Circle = function() { 
	//var this={}
	var obj = new Object();  //obj={}
	var a=100;
	obj.PI = 3.14159;  
	
	obj.area = function( r ) {  
		console.log(a)
		return this.PI * r * r;  
		//this访问了外部函数的变量obj
	}  
	return obj;  
	//return this;
}  
var c = new Circle();  //{PI:3.14159,area:function(){……}}
alert( c.area( 1.0 ) );  

第三种:通过表达式写在对象的方法上

var Circle = new Object();  
Circle.PI = 3.14159;  
Circle.Area = function( r ) {  
       return this.PI * r * r;  
}  
alert( Circle.Area( 1.0 ) );  

第四种:通过属性创建写在对象的方法上

var Circle={  
  PI:3.14159,  
  area:function(r){  
          return this.PI * r * r;  
        }  
};  
alert( Circle.area(1.0) );  

第五种:通过全局变量赋值(类似于第二种写法的原理)

var demo;
function test(){
    var aaa=100;
    function b(){
        console.log(aaa)
    }
    return b;
    //demo=b;
}
demo=test()
test();
demo();

9.4.3 闭包的用途

闭包的用途汇总:

1.实现公有变量

2.可以做缓存

3.可以实现封装,属性私有化

4.实现类和继承

5.模块化开发,防止污染全局变量

实现公有变量

eg:函数累加器

function add() {
    var counter = 0;
    return counter += 1;
}
 
add();//1
add();//1
add();//1
// 本意是想输出 3, 但事与愿违,输出的都是 1 

你可以使用全局变量,函数设置计数器递增:

var counter = 0;
 
function add() {
   return counter += 1;
}
 
add();
add();
add();
 
// 计数器现在为 3

但问题来了,页面上的任何脚本都能改变计数器,即便没有调用 add() 函数。

这时我们需要闭包。

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();
 
add();//1
add();//2
add();//3
 
// 计数器为 3

可以做缓存

eg:eater

function eater() {
	var food="";
	var obj={
		eat : function(){
			console.log("i am eating"+food);
			food="";
		},
		push : function(myfood){
			food=myfood;
		}
	}
	return obj;
}
var eater1 = eater();
eater1.push("banana")
eater1.eat();//i am eating banana

eater2=eater();
eater2.push("apple");
eater2.eat();
eater2.eat();

可以实现封装,属性私有化

eg:Person();

var person=function(){
	var name="default";
	return {
		getName:function(){
			return name
		},
		setName:function(val){
			name=val
		}
	}
}();

console.log(person.name)//undefined
console.log( person.getName() );//default
person.setName("test");
console.log(person.getName());//test

模块化开发,防止污染全局变量

var a=(function(j){
	
	return function{console.log(j)}
    //模块内的函数变量不会被外部函数所污染,且执行完立即被销毁,不会浪费空间
    
}(i))

实现类和继承

function Person(){    
    var name = "default";       
       
    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }
    }    
 };   
var p = new Person();
p.setName("Tom");
alert(p.getName());//Tom

var Jack = function(){};
//修改Jack这个构造函数的属性prototype的引用,让其构造对象可以继承自Person
Jack.prototype = new Person();
//添加私有方法
Jack.prototype.Say = function(){
    alert("Hello,my name is"+name);
};
var j = new Jack();//{}
j.setName("Jack");
j.Say();
alert(j.getName());

9.4.4 闭包的避免

看一下下main函数的执行结果:

 function test() {
 	var arr=[];
 	for(var i=0;i<10;i++){
 		arr[i]=function(){
 			document.write(i+"    ");
 			
 		}
 	}
	return arr;
 }
 var myArr = test();//[function(){ i },function(){ i }]
 for(var j=0;j<10;j++){
 	myArr[j]();
 }

上面函数将在界面上打印出10个10,数组myArr中保存的时test()执行后返回的数组arr,该数组存的是10个函数。

从第12行开始一个for循环,将数组myArr中的每一个函数都取出来调用一遍。

为了可以打印出0~9来,首先考虑内部函数自调用:

 for(var i=0;i<10;i++){
 		arr[i]=function(){
 			document.write(i+"    ");
 			//上面的语句中并不存在赋值,只有在该函数被调用时,上面语句才会去寻找变量i的值
 		}()
 	}

但这是立即执行,执行一遍就没有了。要想达到随时可以调用函数打印出0~9来,看看下面的代码:

for(var i=0;i<10;i++){
	(function(j){
    	arr[j]=function(){
 			document.write(j+"    ");
 		}
	}(i))
}
  • 循环执行10次,就有10个立即执行函数被执行;每个立即执行函数执行后其引用都会被销毁,所以10次循环对应的是10个立即函数。
  • 每个立即执行函数执行时,该立即执行函数中的形参j 都被实参i 所赋值。
  • 在10个立即执行函数执行后,数组arr中保存了10个函数 function(){ document.write(j+" ");}。
  • 这10个函数对应的j 是10个立即执行函数的形参j,所以,数组arr中保存的10个函数中j 的值分别对应的是0~9。

练习:

<ul>
	<li>a</li>
	<li>a</li>
	<li>a</li>
	<li>a</li>
</ul>
<!-- 使用原生js给每个li绑定一个点击事件,点击对应的li,输出其顺序 -->

正解:

<ul>
	<li>商品1</li>
	<li>商品2</li>
	<li>商品3</li>
	<li>商品4</li>
</ul>
<script type="text/javascript">
	// GO{
	// 	i:4
	// }
	for (var i = 0; i < document.querySelectorAll("li").length; i++) {
		document.querySelectorAll("li")[i].onclick = function(i) {
			return function() {
				alert(i + 1);
			}
		}(i)
	}
</script>

9.4.5 本地存储对象

localStorage:本地存储,与整个应用ip+端口 setItem  getItem
localStorage.setItem('属性名','属性值')  localStorage.getItem('属性名')

sessionStorage:本地存储对象,与当前页面(会话)相关,
sessionStorage.setItem('属性名','属性值') sessionStorage.getItem('属性名')
//本地存储对象,可存储空间较大

document.cookie缓存,每一次前后端通信都会携带cookie,值是一个字符串"属性名=属性值",可以设置过期时间expires 可存储空间较小 
	 
<div style="width: 240px; margin: 0 auto; background: linear-gradient(lightpink,skyblue);">
用户名:<input type="text" name="uname" id="uname" value="" /><br />
密码:&nbsp;&nbsp;&nbsp;<input type="password" name="pw" id="pw" value="" /><br />
<button type="button">登录</button>

</div>
<script type="text/javascript">

document.cookie = "psword=sish;";
console.log(document.cookie)
console.log(document.cookie.indexOf("psword"))
var mypath = document.cookie.substring(document.cookie.indexOf("psword"), document.cookie.indexOf(";", document.cookie
	.indexOf("psword")));
console.log(mypath);

document.querySelector("button").onclick = function() {
	var username = document.querySelector('#uname').value;
	var password = document.querySelector('#pw').value;
	if (username == 'zym' && password == '971002') {
		localStorage.setItem('login', '1');
		alert('登陆成功');
	} else {
		alert('用户名密码输入有误!')
	}
}

9.5 函数-特殊的对象

9.5.1 函数的属性

函数是js中特殊的对象,有自带的属性:

函数的属性:toString/valueOf/call/apply/bind/length/prototype…

toString-- 转换为字符串

valueOf – 求值

call / apply / bind / – 改变this指向

length – 函数形参的个数

prototype – 指向原型对象的引用,构造原型链,用于继承

2.prototype:函数属性,指向一个对象的引用,称为原型对象。

函数.length : 该函数形参的个数

aruments.length : 返回实参的个数,
arguments.callee : 返回正在被执行的函数

/* 
函数的属性:toString/valueOf/call/apply/bind/length/prototype...
函数.length:该函数形参的个数

arguments.length:返回实参的个数,
arguments.callee:返回正在被执行的函数
 */
function fn(){
	console.log(arguments.callee==fn);//true
}
fn()
  • length:函数属性,函数形参的个数;

    function fn (a, b, c) {}
    console.log(fn.length);		//3
    

    该属性的经典用法是用于验证参数:

    function calleeLengthDemo(arg1, arg2) {
    	//调用callee,返回当前正在被执行的函数本身,所以arguments.callee.length代表形参
    	//arguments.length代表实参
        if (arguments.length==arguments.callee.length) {
              window.alert("验证形参和实参长度正确!");
            return;
          } else {
              alert("实参长度:" +arguments.length);
              alert("形参长度: " +arguments.callee.length);
          }
    }
    
  • prototype:函数属性,指向一个对象的引用,称为原型对象;

    console.log(Array.prototype);
    

9.5.2 函数的方法

  • call() 和apply():实现方法劫持

每个函数都包含两个非继承而来的方法:call()方法和apply()方法。这两个方法的作用都是一样的。都是在特定的作用域中调用函数,想当于设置函数体内this对象的值,以扩充函数赖以运行的作用域。

作用,改变this指向
区别,传参形式不同

call

    <script>
        window.color = 'red';
        document.color = 'yellow';

        var s1 = {color: 'blue' };
        function changeColor(){
            console.log(this.color);
        }

        changeColor.call();   // red     
        changeColor.call(window);   //red
        changeColor.call(document); //yellow
        changeColor.call(this);    //red
        changeColor.call(s1);       //blue
    </script>
function sum(num1, num2){
    console.log(this);//this指向-window
    return num1 + num2;//20
}
function callSum(num1, num2){
    return sum.call(this, num1, num2);
}
console.log(callSum(10,10));  

bind 创建一个新的函数体,在后面括号里添加this指向的函数

apply

apply()方法接收两个参数:

一个是在其中运行函数的作用域 (或者可以说成是要调用函数的母对象,它是调用上下文,在函数体内通过this来获得对它的引用),

另一个是参数数组。

其中,第二个参数可以是Array的实例,也可以是arguments对象.

function sum(num1, num2){
    return num1 + num2;
}
//因为运行函数的作用域是全局作用域,所以this代表的是window对象
function callSum1(num1, num2){
    //arguments对象:函数对象内,自动创建的专门接收所有参数值得类数组对象arguments = []。
	//arguments[i]: 获得传入的下标为i的参数值
	//arguments.length: 获得传入的参数个数!
    return sum.apply(this, arguments);
}
function callSum2(num1, num2){
    return sum.apply(this, [num1, num2]);
}
console.log(callSum1(10,10));//20
console.log(callSum2(10,10));//20

面试问题:JavaScript的call和apply方法是做什么的,两者有什么区别?

call()和apply(),这两个函数都是在特定的作用域中调用函数,能改变函数的作用域,实际上是改变函数体内“this”的指代的对象。
call() 应用某一对象的一个方法,用另一个对象替换当前对象。
apply()  调用对象的一个方法,另一个对象替换当前对象。
二者作用:
        1、调用函数,传递参数
        2.改变函数的作用域
        3.模拟java中类的继承
二者区别:
        两者传递的参数不同。
        call递的参数时一一列举出来的;
        apply传递参数形式是数组。

练习:call和apply经典用法,实现继承:

function Person(name, age) {
	this.name = name;
	this.age = age;
	this.talk = function() {
		console.log(this.name + '的年龄是' + this.age + '岁');
	}
}
function itgirl(name, code, age) {
	Person.call(this, name, age);
	this.code = code;
	this.mycode = function() {
		console.log('我在学习' + this.code);
	}
}
var girl1 = new itgirl('一个女生', 'H5', 20);
girl1.talk(); //一个女生的年龄是20岁
girl1.mycode(); //我在学习H5

/* itgirl 作用域链
AO(itgirl){
	name:“一个女生”,
	code:"H5",
	age:20,
	this:{
		name:“一个女生”,
		age:20,
		talk:function……,
		code:"H5",
		mycode:function……
	}
}

GO{
	girl1:{
		name:“一个女生”,
		age:20,
		talk:function……,
		code:"H5",
		mycode:function……
	},
	Person:function Person(name, age){……,
	itgirl:function itgirl (name, code, age) {……
} */


/* Person 作用域链:
<!-- AO(Person){
	name:“一个女生”,
	age:20,
	this:-引用地址{
		name:“一个女生”,
		age:20,
		talk:function……
	}

} -->

GO{
	girl1:undefined,
	Person:function Person(name, age){……,
	itgirl:function itgirl (name, code, age) {……
} */

  • toString():返回自定义函数的完整代码。
function fn (a, b, c) {'函数代码'}

var str = fn.toString();
console.log(str);

9.6 高阶函数

高阶函数是指操作函数的函数,一般有以下两种情况:
  1、函数可以作为参数被传递
  2、函数可以作为返回值输出

  • 参数传递(回调函数)
function addnum (a, b, fn) {
    return fn(a) + fn(b); 
}

var a = addnum(20, -30, Math.abs);//50
  • 返回值输出

下面是使用Object.prototype.toString方法判断数据类型的三个isType函数:{}.toString() “object Object”

var isString = function( obj ){
  return Object.prototype.toString.call( obj ) === '[object String]';
};

var isArray = function( obj ){
  return Object.prototype.toString.call( obj ) === '[object Array]';
};

var isNumber = function( obj ){
  return Object.prototype.toString.call( obj ) === '[object Number]';
};

实际上,这些函数的大部分实现都是相同的,不同的只是Object.prototype.toString.call(obj)返回的字符串。为了避免多余的代码,可以把这些字符串作为参数提前传入isType函数。代码如下:

var isType = function( type ){ 
  return function( obj ){
    return Object.prototype.toString.call( obj ) === '[object '+ type +']';
  }
};

var isString = isType( 'String' );
var isArray = isType( 'Array' ); 
var isNumber = isType( 'Number' );

console.log( isArray( [ 1, 2, 3 ] ) );    // 输出:true

复杂点的高阶函数;

//检查一个值是否为偶数
function even (a) {
	return a%2 === 0;	//返回true或false
}
//对函数判断进行反转
function not (f) {
	return function () {
		var result = f.apply(this, arguments);
		console.log(!result);//这里会打印什么那?我很好奇
		return !result;
	}
}

var odd     = not(even);
console.log(odd);
/*odd变量所存储的函数如下:
function odd() {
	console.log(this)
	var result = even.apply(this, arguments);
	console.log(!result);//true true true false
	return !result;
}
*/
var arr     = [1,3,5,4];
var r1      = arr.every(odd);//false,every需要所有的返回值都是true才能返回true,遇到一个返回值是false 的时候,立即返回false,停止迭代。
var r2      = arr.some(odd);//true
console.log(r1);
console.log(r2);

10、正则表达式

正则表达式RegExp(Regular Expression):匹配 特殊字符或有特殊搭配原则的字符 的最佳选择。

转义字符\,在反斜杠\后边放的紧挨着得字符被强制转化成文本

\"-----实现在双引号里再放双引号
\r-----行结束符,即回车
\t-----制表符,键盘得tab键
  • 多行字符串

    eg:	
    	\------还可以转义回车(换行)符号,实现js语法上的多行字符串
    
  • 换行的转义字符

    eg:	
    	\n------实现换行
    

10.1 语法规则

10.1.1 创建方法

两种创建方式:

1、直接量

其本身是一个对象,表达的意义是一种规则。

(1)在两个斜杠中间写规则。

var  reg=/abc/;
var  str="abcd";
reg.test(str)  ;	//true,检查在字符串str中有没有符合reg规则得字符

(2)在正则表达式得双反斜杠后边还可以加字母i、g、m,表达其属性。

//i===》ignorcase,忽略大小写
//eg:
var  reg=/abce/i;
var  str="ABCEd";
reg.test(str)  ;

2、构造方法RegExp()

//使用new操作符,new RegExp();
//eg:
var reg=new RegExp("abc");
var str="abcd";
reg.test(str);

//在new RegExp("abc")函数里边也可以添加属性i、g、m
//eg:
var reg=new RegExp("abc","im");
var str="abcd";

使用new操作符,可以将已经存在的正则表达式用来给函数RegExp()传参,构造新的正则表达式。

//reg与reg1值相同,但两个两个值相互独立,即reg!=reg1
//eg:
var reg=/abce/m;
var reg1=new RegExp(reg);

若去除new操作符,将已经存在的正则表达式用来给函数RegExp()传参,只是传递引用,不能构建新的正则表达式。

//reg与reg1只是对同一个正则表达式的引用
//eg:
var reg=/abce/m;
var reg1=RegExp(reg);
reg.abc=3;
console.log(reg1.abc);//3

10.1.2 三个属性i,g,m

正则表达式的属性(也称修饰符),可以在全局搜索中不区分大小写:

i —(ignoreCase )执行匹配时忽略大小写
g—(global)执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)

m—(multiline)执行多行匹配

//字符串的match(reg),返回值匹配结果
//属性i--忽略大小写 g-全局匹配 m-多行匹配
var reg=/ab/;
var str="daBjdgkabadjgab";
console.log( str.match(reg) );//["ab"]

var reg1=/ab/ig;
console.log( str.match(reg1) );//["aB", "ab", "ab"]
//eg:
var reg=/a/g;
var str="abcdea";
str.match(reg);//["a","a"]
reg=/^a/g;//插入符^指的是以字母a为开头
str.match(reg);//["a"]
str="abcde\na";
str.match(reg);//["a"],还没有多行匹配属性
reg=/^a/gm;
str.match(reg);//["a","a"]

10.1.3 方括号

方括号用于查找某个范围内的字符:

一个中括号代表一位,中括号里边的内容代表的是这一位可以取值的范围;

插入符^放到[]里边表示"非"的意思;

括号里可以加入"|“表示"或"的意思,”|"操作符两边放匹配规则;

//中括号只匹配一位字符,中括号里边放的字符表示的是这一位字符的取值范围
var reg1 = /[0-9]/g;
var str1 = "12309u98";
console.log(str1.match(reg1)); // ["1", "2", "3", "0", "9", "9", "8"]

var reg2 = /(wer|iou)/gi;
var str2 = "1ypiouqwer";
console.log(str2.match(reg2)); //["iou", "wer"]

var reg3 = /([0-9]u|[0-9]z)/g;
var str3 = "12309u98723zpoixc";
console.log(str3.match(reg3)); // ["9u", "3z"]

//插入符^放到[]里边表示"非"的意思
var reg4 = /[^a][^b]/g;
var str4 = "ab1cd";
console.log(str4.match(reg4)); //["b1", "cd"]

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值