Java三要素:事件源,事件类型,事件处理
核心(ECMAScript)
文档对象模型(DOM)Document Object Model
浏览器对象模型(BOM)Browser Object Model
(1)版本
ECMAScript 3.1 成为 ECMA-262 第 5 版,并于 2009 年 12 月 3 日正式发布。第 5 版力求澄清第 3版中已知的歧义并增添了新的功能。新功能包括原生 JSON 对象(用于解析和序列化 JSON 数据)、继承的方法和高级属性定义,另外还包含一种严格模式,对 ECMAScript 引擎解释和执行代码进行了补充说明。
(2)ECMAScirpt兼容性
(3)Web 浏览器对 ECMAScript 的支持
五大主流浏览器–内核:
IE – trident
Firefox – gecko
safari – webkit
chrome – webkit/blink
oprera – presto
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.行内
给标签设置对应的属性,属性值是要执行的JavaScript代码
<body>
<div style="height: 1000px;" onclick="alert('弹出一个警告框')">div>
<a href="javacsript:void(0)">连接标签a>
body>
2.内嵌
使用script标签,标签需要闭合,标签内容是要执行的JavaScript代码。
<body>
<div>页面div元素div>
<script type="text/javascript">
alert('页面一打开弹出一个警告框');
script>
body>
3.外联
使用script标签,标签需要闭合,设置属性src,src的值是js文件路径。
<body>
<div>页面元素div>
<script src="00js外联.js" type="text/javascript" charset="UTF-8">script>
body>
注:
① 嵌入和导入的数量不受限制;
② 使用script 标签引入外部js文件时(标明src属性,不管有没有赋值),标签内容部分再填写js语句是不能执行的;
③ js代码直接写在一个独立的文件里面,该文件就是js文件,后缀是.js
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>
<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. 获取页面元素
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'
加号运算符可用于连接字符串:
var name ='花花';
var sex ='男';
var s = name+'是一个'+sex+'生';
console.log(s);
document.write(s);
事件是指可以被JS监测到的网页行为;如:鼠标点击、鼠标移动、鼠标移入/离开、键盘按键、页面加载等;
JavaScript事件的三要素:事件源、事件、事件处理
H5特定事件列表:
属性 | 值 | 描述 |
---|---|---|
offline | script | 文档进入离线状态时触发。 |
onabort | script | 事件中断时触发。 |
onafterprint | script | 文档被打印后触发。 |
onbeforeprint | script | 文档被打印前触发。 |
oncanplay | script | 媒体停止缓冲,可以开始播放时触发。 |
oncanplaythrough | script | 媒体可以播放到结束时触发,无需停止缓冲。 |
onchange | script | 元素发生变化时触发。 |
onclick | script | 鼠标点击触发。 |
ondblclick | script | 双击鼠标时触发。 |
ondrag | script | 元素被拖动时触发。 |
ondragend | script | 拖拽操作结束时触发。 |
ondragover | script | 元素被拖放到有效目标上时触发。 |
ondragstart | script | 拖拽操作开始时触发。 |
onended | script | 媒体到达终点时触发。 |
onerror | script | 发生错误时触发。 |
onforminput | script | 表单获得用户输入时触发。 |
onhaschange | script | 文档变化时触发。 |
oninput | script | 元素获得用户输入时触发。 |
oninvalid | script | 元素失效时触发。 |
onkeydown | script | 键盘按下时触发。 |
onkeyup | script | 按键释放时触发。 |
onload | script | 载入文档时触发。 |
onloadeddata | script | 载入媒体数据时触发。 |
onloadedmetadata | script | 媒体元素的媒体数据载入时触发。 |
onloadstart | script | 浏览器开始载入媒体数据时触发。 |
onmessage | script | 消息被触发时触发。 |
onmousedown | script | 鼠标按键被按下时触发。 |
onmousemove | script | 鼠标指针移动时触发。 |
onmouseout | script | 鼠标指针移出元素时触发。 |
onmouseover | script | 鼠标指针移入元素时触发。 |
onmouseup | script | 鼠标按键释放时触发。 |
onmousewheel | script | 鼠标滚轮转动时触发。 |
onoine | script | 文档上线时触发。 |
ononline | script | 文档上线时触发。 |
onpagehide | script | 窗口隐藏时触发。 |
onpause | script | 媒体数据暂停时触发。 |
onplay | script | 媒体数据开始播放时触发。 |
onplaying | script | 媒体数据播放时触发。 |
onprogress | script | 浏览器获取媒体数据时触发。 |
onratechange | script | 媒体数据的播放比率改变时触发。 |
onredo | script | 文档执行 redo 操作时触发。 |
onresize | script | 调整窗口尺寸时触发。 |
onscroll | script | 元素的滚动条滚动时触发。 |
onselect | script | 元素被选中时触发。 |
onstalled | script | 获取媒体数据发生错误时触发。 |
onstorage | script | 载入文档时触发。 |
onsubmit | script | 表单提交时触发。 |
onsuspend | script | 浏览器获取媒体数据,但获取整个媒体文件中止时触发。 |
ontimeupdate | script | 媒体播放位置改变时触发。 |
onundo | script | 文档执行 undo 操作时触发。 |
onunload | script | 用户离开文档时触发。 |
onvolumechange | script | 媒体音量发生变化,包括设置为“静音”时触发。 |
onwaiting | script | 媒体停止播放,等待恢复时触发。 |
<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>
具有特定用途的关键字:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aqLilYUr-1592232189686)(C:\Users\Administrator\Desktop\培训\JS\assets\importantword.png)]
不能用作标识符的保留字:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uyn3mcco-1592232189690)(C:\Users\Administrator\Desktop\培训\JS\assets\es3-baoliuword.png)]
<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>
<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>
JavaScript的数据类型分类:
原始类型:数字、字符串、布尔值、null(空)、undefined(未定义);存储于栈内存。
引用类型(也称对象类型):原始类型之外的类型,如数组、对象、函数等;存储于堆内存。
<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>
检测给定变量的数据类型。
对一个值使用 typeof 操作符可能返回下列某个字符串:
<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>
在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined。
取变量(不管是取空间还是空间里的值) ,先看这个空间有没有,再看有没有值:声明了一个空间,但是没有值的空间,再取值就会取到undefined;取一个不存在的空间的数据时,程序则报错。
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>
该类型只有两个字面值: 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>
这种类型用来表示整数和浮点数值(浮点数值在某些语言中也被称为双精度数值)。
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。
如果是字符串,遵循下列规则:
如果是对象,则调用对象的 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>
字符串可以由双引号(")或单引号(’)表示。
var firstName = "Nicholas";
var lastName = 'Zakas';
1.字符字面量字符串
字 面 量 | 含 义 |
---|---|
\n | 换行 |
\t | 制表 |
\b | 空格 |
\r | 回车 |
\f | 进纸 |
\ | 斜杠 |
’ | 单引号(’),在用单引号表示的字符串中使用。例如: ‘He said, ‘hey.’’ |
" | 双引号("),在用双引号表示的字符串中使用。例如: “He said, “hey.”” |
// HTML的转义字符:
// < >
// 引号引起来的就是字符串string
console.log(" + "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(`
${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("
");
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);
创建 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。这段代码说明了在读取字符串、数字和布尔值的属性值或方法(实际上是它们对应包装对象的属性值或方法)表现的像对象一样。但如果你试图给属性赋值,则会忽略这个操作:修改只是发生在临时对象身上,而这个临时对象并不会继续保留下来。
不可变的原始值
– 任何方法都无法更改一个原始类型的值:数字、字符串、布尔值、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]
变量的作用域(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();
原始表达式不可再分割,是最小单位的表达式;简单的概念性东西,知道即可;
原始表达式包含直接量、关键字(保留字)和变量名;
//直接量
1;
1.02;
'hello world!';
//保留字
true;
false;
this;
//变量名
name;
age;
可以简单理解为:数组直接量;
数组初始化表达式:由中括号([])和其内用逗号(英文状态 ,)分隔开的列表构成;
初始化的结果是创建一个新的数组;数组的元素是逗号分隔开的表达式的值;
“数组初始化表达式”中的“元素”也可以是“数组初始化表达式”,即多维数组
//数组初始化表达式
[];
[1,2,3,4,'a','b','c'];
[['a','b','c'],['d','e','f'],['g','h']];
//中间省略的元素会怎样?最后省略的元素又会怎样?
[1,,,,6,,,]
//对象初始化表达式
{name:'yourname', age:22};
{
p1:{name:'华子', age:22},
p2:{name:'华耀', age:25},
p3:{name:'华赓', age:24}
};
函数定义表达式定义一个JavaScript函数
var fn = function (a, b) {
return a+b;
}
fn()
//函数声明
//function fn(a, b) {
// return a+b;
//}
//fn();
属性访问表达式得到一个对象的属性或者一个数组的元素值
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]);
调用表达式fn() 是执行函数或方法的语法表示;
fn(1, 2);
Math.max(1,2,3);//3
a.sort();
由函数调用表达式延伸而来,前面加个new即可;
如果不需要传递参数,函数后面的小括号可以省略;
不看个例子感觉不踏实:
new Array(1,2,3); //数组长度为3,元素为[1,2,3]
new Array(3) //数组长度为3,元素为空[,,,]
new String('hello world!');
Number()/Boolean()/Object()/Function()
加减乘除取余,+ - * / % ;
+:数字相加 或 字符串连接;
加法操作的行为表现:
一、如果其中一个操作数是对象,则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类型.
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
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
1. 逻辑非
逻辑非操作符由一个叹号(!)表示。
这个操作符都会返回一个布尔值。逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反。也就是说,逻辑非操作符遵循下列规则:
alert(!false);
alert(!"blue");
alert(!0);
alert(!NaN);
alert(!"");
alert(!12345);
2. 逻辑与
逻辑与操作符由两个和号(&&)表示,有两个操作数。
console.log(123 && null); //null
逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况下,逻辑与操作就不一定返回布尔值;此时,它遵循下列规则:
var found = false;
var result = (found && someUndefinedVariable);
alert(result);
3. 逻辑或
逻辑或操作符由两个竖线符号(||)表示,有两个操作数.
与逻辑与操作相似,如果有一个操作数不是布尔值,逻辑或也不一定返回布尔值;此时,它遵循下列规则:
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
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
与另外两个乘性操作符类似,求模操作符会遵循下列特殊规则来处理特殊的值:
与乘性操作符类似,加性操作符也会在后台转换不同的数据类型。
1. 加法
如果两个操作符都是数值,执行常规的加法计算,然后根据下列规则返回结果:
不过,如果有一个操作数是字符串,那么就要应用如下规则:
2. 减法
与加法操作符类似, ECMAScript 中的减法操作符在处理各种数据类型转换时,同样需要遵循一些
特殊规则,如下所示:
// 加号: + , 操作出来的结果是字符串/数字
// 减法: - , 操作出来的结果都是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);
关系运算符用于检测两个值之间的关系,总是返回一个布尔值true或false。
小于(<) 、大于(>) 、小于等于(<=)和大于等于(>=)这几个关系操作符用于对两个值进行比较。
当关系操作符的操作数使用了非数值时,也要进行数据转换或完成某些奇怪的操作。以下就是相应的规则。
/* 关系运算符,操作结果都是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
/*
1. 相等和不相等
在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:
这两个操作符在进行比较时则要遵循下列规则。
2.全等和不全等
全等操作符由 3 个等于号(===)表示,它只在两个操作数未经转换就相等的情况下返回 true。
不全等操作符由一个叹号后跟两个等于号(!==)表示,它在两个操作数未经转换就不相等的情况下返回 true。
null == undefined 会返回 true,因为它们是类似的值;但 null === undefined 会返回 false,因为它们是不同类型的值。
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
检查左侧的对象是否是右侧类的实例,如果是返回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
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。
简单的赋值操作符由等于号(=)表示,其作用就是把右侧的值赋给左侧的变量.
如果在等于号(=)前面再添加乘性操作符、加性操作符或位操作符,就可以完成复合赋值操作。
这种复合赋值操作相当于是对下面常规表达式的简写形式:
var num = 10;
num = num + 10;
其中的第二行代码可以用一个复合赋值来代替:
var num = 10;
num += 10;//num=num+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。
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
条件语句用于基于不同的条件来执行不同的动作。
在 JavaScript 中,我们可使用以下条件语句:
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)
{
当条件 1 为 true 时执行的代码
}
else if (condition2)
{
当条件 2 为 true 时执行的代码
}
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("晚上好");
}
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("不及格");
}
JavaScript 支持不同类型的循环:
for(语句一;语句二;语句三){
//代码块
}
var arr = [1, 2, 3];
var i = 0;
for (; i < arr.length; i++) {
console.log(arr[i]);
}
通常我们会使用语句 1 初始化循环中所用的变量 (var i=0)。
语句 1 是可选的,也就是说不使用语句 1 也可以。
您可以在语句 1 中初始化任意(或者多个)值:
通常语句 2 用于评估初始变量的条件。
语句 2 同样是可选的。
如果语句 2 返回 true,则循环再次开始,如果返回 false,则循环将结束。
// 语句二可省,但是一定要添加控制条件,避免死循环
在每次循环(代码块)已被执行之后执行。
通常语句 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] + "
");
}
/*2、写个乘法口诀表; */
var sum = 0;
for (i = 1; i <= 9; i++) {
for (j = 1; j <= i; j++) {
sum = i * j;
document.write(i +"*"+ j +"="+sum+" ");
}
document.write("
");
}
/* 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}
`);
}
}
}
}
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块
<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] + "
";
}
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>
do/while 循环是 while 循环的变体。该循环会在检查条件是否为真之前执行一次代码块,然后如果条件为真的话,就会重复这个循环。
do {
//代码块
} while ( //判断条件)
使用 do/while 循环。该循环至少会执行一次,即使条件为 false 它也会执行一次,因为代码块会在条件被测试前执行:
function myFunction() {
var x = "",
i = 0;
do {
x = x + "该数字为 " + i + "
";
i++;
} while (i < 5)
document.getElementById("demo").innerHTML = x;
}
/*
该数字为 0
该数字为 1
该数字为 2
该数字为 3
该数字为 4
*/
while 和 do/while 的区别:
//条件为假, do while至少执行一次,
//条件为真, while和do while执行次数一致
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);
标签语句,在语句之前加上冒号:可标记 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] + "
");
document.write(cars[1] + "
");
document.write(cars[2] + "
");
break list;
document.write(cars[3] + "
");
document.write(cars[4] + "
");
document.write(cars[5] + "
");
}
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() );
立即停止正在执行的程序,跳转至就近的逻辑异常处理程序。
// 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 字符串、数字、逻辑值或对象。
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
with语句用于临时扩展作用域链。
临时扩展作用域,临时;with代码块执行完成后恢复原始状态。
with(obj){
//语句块
}
//with 临时扩展语句块的作用域为obj对象
with(document.querySelector("div").style){
backgroundColor="pink";
width="200px";
height="200px";
borderRadius="50%";
margin="0 auto";
}
不推荐使用with,代码不好优化、运行慢等问题;
并且,严格模式下是不能使用with。
JavaScript 对象是拥有属性和方法的数据。
已经学习了 JavaScript 变量的赋值。
var car = "Fiat";
对象也是一个变量,但对象可以包含多个值(多个变量)。
var car = {
type:"Fiat",
model:500,
color:"white"
};
3 个值 (“Fiat”, 500, “white”) 赋予变量 car,3 个变量 (type, model, color) 赋予变量 car。
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]);
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。
// 增添属性: 给对象没有的属性赋值
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
(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); //<<夏洛特烦恼>>
序列化对象是指将对象的状态转成字符串,也可以将字符串还原为对象;
(a) 转成字符串:JSON.stringify();
(b) 还原为对象:JSON.parse();
this是JavaScript语言中定义的众多关键字之一,它的特殊在于它自动定义于每一个函数域内。
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并没有引用函数的词法作用域。
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
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)//"美国" "德国"
ECMAScript需要通过原型链来实现继承。
每一个对象都从原型继承属性,直到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)
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
JavaScript 中的数组是一种特殊的对象,用来表示偏移量的索引是该对象的属性,索引只能是整数。
这些数字索引在内部被转换为字符串类型,因为 JavaScript 对象中 的属性名必须是字符串。
创建数组的基本方式有两种。
(1) 第一种是使用Array构造函数:
var colors1 = new Array(); //定义空数组
var colors2 = new Array(20); //定义长度为20的数组
(2) 第二种方式是使用数组字面量表示法:
var colors = ['red','blue','green'];
var names = [];
推荐使用 [] 操作符,和使用Array的构造函数相比,编写代码的速度更快,效率更高。
写–使用 [] 操作符将数据赋给数组:
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);
元素的添加:
(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);
将一个数组赋给另外一个数组。
两种方式:浅复制和深复制。
浅复制
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
JavaScript 提供了一组用来访问数组元素的函数,叫做存取函数。
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"]
数组元素的增删:
– 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) 方法从数组中删除元素。
//1. .reverse()
var arr = [1, 200, 300, 3, 5];
console.log(arr.reverse());
//[5, 3, 300, 200, 1]
// 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"]
这些方法的参数都是函数,可以对数组中的每一个元素运用该函数,原数组不变。
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
reduce和reduceRight:使用指定的函数对数组元素进行组合,生成一个值,接收两个参数。
详细解释:
– 参数一:要执行的函数,有返回值,函数内部处理的参数如下
– 参数二:传递给函数的默认值,可忽略
– 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
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');
类数组对象:
只包含使用从零开始,且自然递增的整数做键名,并且定义了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);
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
*/
多维数组,外层数组的每一项也是一个数组;
外层数组看作行,内层数组看作列。
通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。
函数即对象,程序可以随意操控它们。
函数可以嵌套在其他函数中定义,这样它们就可以访问它们被定义时所处的作用域中的任何变量。
一处定义,处处调用;
– 如果把函数作为一个对象的属性,则称为方法;
– 每次调用函数会产生一个this:谁调用这个函数或者方法,this就指向谁;
– 函数就是对象,可以给他设置属性或方法;
总共有三种函数定义的方式:函数声明语句、函数表达式、内置构造函数。
函数声明语句:
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 定义。
函数同样可以通过内置的 JavaScript 函数构造器(Function())定义。
用Function()构造函数创建一个函数时并不遵循典型的作用域,它一直把它当作是顶级函数来执行。
var y = "global";
function constructFunction() {
var y = "local";
function test(){};
return new Function("return y"); // 无法获取局部变量,作用域始终是全局作用域
}
alert(constructFunction()());
函数可以嵌套在其他函数里面,也就是在函数里面可以定义函数。
被嵌套的函数可以访问嵌套他们的函数的变量或参数。
javascript一共有4种调用模式:
函数调用模式、方法调用模式、构造器调用模式 和 间接调用模式。
每种方式的不同在于 this 的初始化。
var myfunc = function(a,b){
console.log(this);
return a+b;
}
alert(myfunc(3,4)); //window.myfunc
函数调用模式–结论:
a, this是指向Window的
b, 返回值是由return语句决定的,如果没有return则表示没有返回值
先定义一个对象,然后在对象的属性中定义方法,通过myobject.property来执行方法。
var name = "james";
var obj = {
name : "wade",
fn1 : function () {
console.log(this.name);
}
};
obj.fn1();
方法调用模式 ( 括号调用 )–结论:
a, this 是指向调用该方法的对象
b, 返回值还是由函数内部return语句决定,如果没有return表示没有返回值
如果函数或者方法调用之前带有关键字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. 添加返回值若为引用类型, 返回值为手动返回的引用值;
也称之为“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);
}
(1,2,3,4)
//浏览器报错么?为什么。
//不报错,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
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);
函数参数分为两类:函数显式参数(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)
js完成解释执行分为三个步骤:
1.语法分析;
2.预编译(全局预编译、函数预编译);
3.执行语句。
举个例子瞧一瞧:
{
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() {}
}
语法分析
符号、大括号等语法检查;
函数预编译
变量声明提升,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() {}
}
解释执行
再看一眼函数:
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() {}
全局“即从页内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
}
该例子的预编译过程:
1、GO{}
2、GO{
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
}
执行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。
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() {}
} */
闭包:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
面试题:闭包是什么?
答:闭包是可访问上一层函数作用域里变量的函数,即便上一层函数已经关闭。
/*
函数闭包:返回的子函数可以访问上一级函数的局部变量
*/
function a() {
var num=100;
function b() {
num++;
console.log(num);//101 102
}
return b;
}
var demo=a();
demo();
demo();
/* b(demo)函数作用域链
AO(a){
num:102,
b:function b() {……
}
GO{
demo:function b() {……,
a:function a() {……
}
GO{
demo:function b() {……,
a:function a() {……
} */
简单的说,Javascript允许使用内部函数—即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。
优点:可以访问局部变量。
缺点:局部变量一直占用内存,内存占用严重,还容易造成内存泄漏(内存被占用,剩余的内存变少,程序加载、处理速度变慢).
/* 闭包:返回的子函数能够访问外部函数的变量和方法,就是一个闭包
优点--能够通过闭包访问操作局部变量
缺点--局部变量一直占用内存,容易造成内存泄漏(只要函数还存在它就有作用域链,函数作用域链上始终保存着外部函数的执行期上下文)
*/
闭包的写法汇总:
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();
闭包的用途汇总:
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());
看一下下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))
}
练习:
<ul>
<li>ali>
<li>ali>
<li>ali>
<li>ali>
ul>
正解:
<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>
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 />
密码: <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('用户名密码输入有误!')
}
}
函数是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);
每个函数都包含两个非继承而来的方法: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 作用域链:
GO{
girl1:undefined,
Person:function Person(name, age){……,
itgirl:function itgirl (name, code, age) {……
} */
function fn (a, b, c) {'函数代码'}
var str = fn.toString();
console.log(str);
高阶函数是指操作函数的函数,一般有以下两种情况:
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);
正则表达式RegExp(Regular Expression):匹配 特殊字符或有特殊搭配原则的字符 的最佳选择。
转义字符\,在反斜杠\后边放的紧挨着得字符被强制转化成文本
\"-----实现在双引号里再放双引号
\r-----行结束符,即回车
\t-----制表符,键盘得tab键
多行字符串
eg:
\------还可以转义回车(换行)符号,实现js语法上的多行字符串
换行的转义字符
eg:
\n------实现换行
两种创建方式:
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
正则表达式的属性(也称修饰符),可以在全局搜索中不区分大小写:
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"]
方括号用于查找某个范围内的字符:
一个中括号代表一位,中括号里边的内容代表的是这一位可以取值的范围;
插入符^放到[]里边表示"非"的意思;
在括号里可以加入"|“表示"或"的意思,”|"操作符两边放匹配规则;
//中括号只匹配一位字符,中括号里边放的字符表示的是这一位字符的取值范围
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"]