首页
留言
友情链接
Search
1
如何使用JavaScript获取和设置CSS root变量值
830 阅读
2
春和 《江海共余生》
325 阅读
3
清除浮动,单行多行超出用...
295 阅读
4
Centos7 下编译安装php8.2
285 阅读
5
中国历史朝代顺序图
228 阅读
分享
Web前端
html&css
javascript
Vue
shopify
shoplazza
后端
ThinkPHP
YII2
服务器端
软件安装
问题合集
历史
故事
诗词
生活
学习
其他
抖音
快手
小视频
随笔
易经
书摘
登录
/
注册
Search
标签搜索
诗词
sunshine
累计撰写
142
篇文章
累计收到
14
条评论
首页
栏目
分享
Web前端
html&css
javascript
Vue
shopify
shoplazza
后端
ThinkPHP
YII2
服务器端
软件安装
问题合集
历史
故事
诗词
生活
学习
其他
抖音
快手
小视频
随笔
易经
书摘
页面
留言
友情链接
搜索到
46
篇与
的结果
2022-10-13
javascript高级-正则表达式
第1章 什么是正则表达式1、概述正则表达式(regular expression)正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来对字符串的进行匹配。用途:检查一个字符串中是否含有某种子串将匹配的某种子串做替换从某个字符串中取出符合某个条件的子串等。“996.icu”1、判断有没有992、判断有没有小数点.3、icu 替换为 tsc // ‘996.icu’.replace(/icu/, ‘tsc’)4、获取其中的9 和 u2、应用场景(1)表单验证里面,验证字符的合法性,如邮箱是否合法,手机号是否合法等等。(2)信息过滤,如论坛帖子或者评论中的非法字符,例如 sql注入、js脚本注入、煽动性的言论。(3)信息采集,采集别人网站上面的内容,例如整页采集时 筛选出需求的部分(4)信息替换。(5)页面伪静态的规则(url重写)。3、入门案例 //匹配str字符串中p是否存在 var str = 'php'; var result = str.match(/p/); //var result = str.match(/参数就是正则表达式/); //返回包含匹配结果的数组或者null其中,正则表达式前后的 斜杠/ 叫做定界符,是固定格式。 //匹配str字符串中所有p var str = 'php'; var result = str.match(/p/g);//进行全局匹配,匹配所有g是global,表示全局的意思,它是正则表达式语法中的修饰符,修饰符应该放到定界符(/)的后面。第2章 正则语法-元字符正则表达式中的字符:元字符:一些具有特殊含义的特殊符号。普通字符:包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。正则表达式三步走① 匹配符(查什么)(等价符、字符簇、修饰符、转义符)② 限定符(查多少)③ 定位符(从哪查)1、限定符限定符(量词)用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种。*匹配前面的子表达式零次或多次。例如,zo 能匹配 "z" 以及 "zoo"。 等价于{0,}。+匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。?匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 、 "does" 中的 "does" 、 "doxy" 中的 "do" 。? 等价于 {0,1}。{n}n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。{n,}n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。{n,m}m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "foooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。 var str = 'phpphp'; var res = str.match(/p+/g);//匹配所有的一个p或多个连在一起的p console.log(res);//["p", "pp", "p"]贪婪匹配与非贪婪匹配:默认情况下,正则表达式执行贪婪匹配(尽可能取多的情况)非贪婪匹配:相对于贪婪匹配来说的。设置方式:将?加在其他限定符之后。 var str = 'phpphp'; var res = str.match(/p+?/g);//+后面有? 执行非贪婪匹配(最少匹配) console.log(res);//["p", "p", "p", "p"] 非贪婪匹配使用场景举例:<div id="div1"><div id="div2">abc</div></div>匹配div2标签2、等价符.匹配除换行符(\n、\r)之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用像"**(.\\n)**"的模式。\d匹配一个数字字符。等价于 [0-9]。\D匹配一个非数字字符。等价于 [ ^0-9]。\w匹配一个字母、数字、下划线。等价于[A-Za-z0-9_]。\W匹配非(字母、数字、下划线)。等价于 [ ^A-Za-z0-9_]。\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。\S匹配任何非空白字符。等价于 [ ^ \f\n\r\t\v]。\n匹配一个换行符。等价于 \x0a 和 \cJ。\r匹配一个回车符。等价于 \x0d 和 \cM。\t匹配一个制表符。等价于 \x09 和 \cI。常用的: . \d \w var str = 'php1js22'; var res = str.match(/\d+/g);//匹配所有的数字 console.log(res);//["1", "22"]3、定位符定位符用来描述字符串或单词的边界,^ 和 $ 分别指字符串的开始与结束,\b 描述单词的前或后边界,\B 表示非单词边界。^匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与 \n 或 \r 之后的位置匹配。$匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与 \n 或 \r 之前的位置匹配。\b匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。\B匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。注意: 通常在表单数据验证时,严格检测字符串格式,需要使用^$ var str = '1234'; var res = str.match(/^\d+$/);//匹配整个数字字符串(匹配id参数值) console.log(res);//["1234"] 4、字符簇(方括号)方括号表示一个范围,也称为字符簇,匹配满足条件的一个字符。[xyz]字符集合。匹配所包含的任意一个字符。例如, [abc]可以匹配 "plain" 中的 'a'。[^xyz]负值字符集合。匹配未包含的任意字符。例如, [^abc] 可以匹配 "plain" 中的'p'、'l'、'i'、'n'。[a-z]字符范围。匹配指定范围内的任意字符。例如, [a-z] 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。[^a-z]负值字符范围。匹配任何不在指定范围内的任意字符。例如,[^a-z]可以匹配任何不在 'a' 到 'z' 范围内的任意字符。x | y或的用法:匹配 x | y。例如,'z | food' 能匹配 "z" 或 "food"。'(z | f)ood' 则匹配 "zood" 或 "food"。字符范围 参考ASCII码表示例:[0-9] 查找任何从 0 至 9 的一个数字。[a-z] 查找任何从小写 a 到小写 z 的字符。[A-Z] 查找任何从大写 A 到大写 Z 的字符。[A-z] 查找任何从大写 A 到小写 z 的字符。包括[ \ ]^_`等六个字符。[A-Za-z]查找任何从大写 A 到小写 z 的字符,不包括[ \ ]^_`等六个字符。var str = 'adcd1234ABCD'; var res = str.match(/[a-z]+/g);//匹配所有小写字母字符串 console.log(res);//["abcd"] 5、修饰符修饰符的用法,修饰符一定要写到正则表达式末尾/之后,可以一次性使用多个修饰符。i 执行对大小写不敏感的匹配。实际上就是不区分大小写的匹配(默认区分大小写)g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。m 执行多行匹配(^和$能匹配每行的开始与结束)。/i用法示例:var str = 'adcd1234ABCD'; var res = str.match(/[a-z]+/gi);//匹配所有字母字符串(忽略大小写) console.log(res);//["adcd", "ABCD"]/m用法示例 var str = '1234\r\n5678'; var res = str.match(/^\d+$/gm);//匹配整个数字字符串 console.log(res);//["1234", "5678"]6、转义字符(\)\将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\ \ ' 匹配 "\ " 而 "\ (" 则匹配 "("。 如果匹配的字符串在正则中有特殊含义的都必须加转义字符。如[]$.*?+|^{}()但是不要乱加转义。 var str = 'php.php';//目标字符串中包含普通字符. var res = str.match(/p\.p/g);//匹配普通字符. 需要转义 console.log(res);//["p.p"]匹配图片名称 var str = '123.jpg';//目标字符串中包含普通字符. var res = str.match(/.*\.jpg/g);//匹配jpg后缀的图片名称 console.log(res);//["123.jpg"]练习题:匹配字符串开始和结束的数字 预期结果 123 789; ^ \d | $var str = '123abc456def789'; var res = str.match(/(^\d+)|(\d+$)/g);//匹配开始或结束的数字 console.log(res);//["123","789"]第3章 js中正则对象正则对象两种定义方式:js中正则表达式两边不用加引号 var pattern = /[a-z]/;//将正则表达式直接当做对象使用。 var pattern = new RegExp(/[a-z]/);//实例化RegExp对象1、test方法test方法检测目标字符串和正则表达式是否匹配,如果匹配返回true,不匹配返回false。正则表达式中,一般不需要加全局修饰符g。 var str = 'php1js22'; var pattern = /^[a-z]/;//以小写字母开头 var res = pattern.test(str);//匹配字符串是否以小写字母开头 console.log(res);//true2、exec方法exec方法执行一个正则匹配,只匹配一次,匹配到结果就返回一个数组类型的结果,匹配不到就返回null。正则表达式中,一般不需要加全局修饰符g。即使正则表达式中,使用了全局匹配修饰符g,也只匹配一次。 var str = 'php1js22'; var pattern = /[a-z]/;//匹配小写字母字符串 //var pattern = /[a-z]/g;//全局匹配小写字母字符串 var res1 = pattern.exec(str); console.log(res1);//["p"] 只匹配一次,返回数组3、lastIndex属性表示正则表达式,上一次匹配结束后的位置(目标字符串中的索引),即下一次匹配开始的位置。lastIndex是一个整数,。没有更多匹配重置lastIndex为0.test 方法和exec方法默认都只匹配一次。匹配后lastIndex 被自动重置为0.特殊情况:如果正则表达式使用了全局匹配修饰符g,则lastIndex不会被重置为0. var str = 'php1js22'; //var pattern = /[a-z]/;//匹配小写字母字符串 var pattern = /[a-z]/g;//全局匹配小写字母字符串 var res1 = pattern.exec(str); console.log(res1);//["p"] 只匹配一次,返回数组 console.log(pattern.lastIndex);//1 下一次匹配开始的位置 var res2 = pattern.exec(str); console.log(res2);//["h"] //从位置1开始匹配案例一1、检测手机号格式手机号特点:11位,纯数字,1开头, [23位号段,具体咨询各大运营商]简单版(限制前两位):1开头,第二位3-9,后面9位数字/^1[3-9]\d{9}$/精确版(限制前三位):(如果出现新的手机号段,则不适用,需更新)示例:13[0-9]开头、14[579]开头、15[0-3,5-9]开头、166开头、17[0135678]开头、18[0-9]开头、19[89]开头/^1(3[0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|8[0-9]|9[89])\d{8}$/手机号检测案例:register.html2、检测邮箱格式邮箱特点:2342.3s_-df@sina.com.cn通用的邮箱的特点:简单版:必须有@ 正则 /@/精确版: 必须有@和. @前面是邮箱名,要求至少一个字符,要求是数字、字母、下划线、[还可以用.-],但是开头必须是数字字母下划线 @和点之间:是一个域名,要求至少一个字符,可以是数字字母中横线,要求开头是数字或字母 点后面:要求是至少一个字符,必须是字母 点xxx,这部分可以出现一次,也可以出现多次最后一个点后面,字符长度为2-6的字母var pattern = /^\w[\w\.-]*@[0-9a-z][0-9a-z-]*(\.[a-z]+)*\.[a-z]{2,6}$/i;register-email.html第4章 分组/捕获和反向引用捕获和反向引用的语法的解释:子表达式在正则表达式中,通过一对圆括号括起来的内容,我们就称之为“子表达式”。如:`var reg = /\d(\d)\d/gi;`捕获(分组)在正则表达式中,子表达式匹配到相应的内容时,系统会自动捕获这个行为,然后将子表达式匹配到的内容放入系统的缓存区中。我们把这个过程就称之为“捕获”。反向引用在正则表达式中,我们可以使用\n(n>0,正整数,代表系统中的缓冲区编号)来获取缓冲区中的内容,我们把这个过程就称之为“反向引用”。在正则语法中,用“\1”来引用前面的捕获(使用子表达式匹配的结果)。用\2表示第二个捕获的内容….在正则语法外(如replace时),用“$1”来引用前面的捕获。 var str = '1122 3434 5566 7879 9887'; //匹配连续四个数字,第一和第二数字相同,第三和第四数字相同 var res = str.match(/(\d)\1(\d)\2/g); console.log(res); //匹配连续四个数字,第一和第三数字相同,第二和第四数字相同 var res = str.match(/(\d)(\d)\1\2/g); console.log(res); //匹配连续四个数字,第一和第三数字相同 var res = str.match(/(\d)\d\1\d/g); console.log(res); //匹配连续四个数字,第一和第二数字相同,第三和第四数字相同,并将相同的数字只保留一个 var res = str.replace(/(\d)\1(\d)\2/g, '$1$2'); console.log(res);禁止引用(?:正则) 这个小括号中的内容不能够被引用 //第一次捕获禁止引用 var str = '1122 3434 5566 7879 9887'; var res = str.match(/(?:\d)(\d)\1\d/g); console.log(res);第5章 匹配中文(utf-8编码)每个字符(中文、英文字母、数字、各种符号、拉丁文、韩文、日文等)都对应着一个Unicode编码。查看Unicode编码,找到中文的部分,然后获取中文的Unicode编码的区间,就可以用正则匹配了。前面我们用[a-z]表示小写字母,[0-9]表示数字,这就是一个范围表示,如果有一个数x能够表示第一个中文,有一个数y能够表示最后一个中文,那么[x-y]就可以表示所有的中文了。中文的Unicode编码从4E00开始,到9FA5结束。[\u4E00-\u9FA5]这个区间就能够表示中文。完整的Unicode编码表:http://blog.csdn.net/hherima/article/details/9045861 var str = "你好,世界"; var res = str.match(/[\u4E00-\u9FA5]/g); console.log(res);//["你", "好", "世", "界"]案例二解决结巴程序把“今今今天晚晚晚晚晚晚上吃吃吃吃吃吃鸡”字符串换成单字的形式,即“今天晚上吃鸡”;核心思想:匹配到重复的字符时,保留一个。 var str = "今今今天晚晚晚晚晚晚上吃吃吃吃吃吃鸡"; //今天晚上吃鸡 var res = str.replace(/([\u4E00-\u9FA5])\1+/g, '$1'); console.log(res);第6章 环视也叫 预查、断言、零宽断言。正则表达式中,用于查找某些内容之前或者之后的东西,叫做环视。环视通常也叫做预查、断言或者零宽断言。1、正向肯定预查也叫 顺序肯定环视every(?=n) 匹配任何其后紧接指定字符串 n 的字符串。 //匹配后面字符为10的win var str = "win7 win8 win10"; var res = str.match(/win(?=10)/g); console.log(res); //["win"]2、正向否定预查也叫 顺序否定环视every(?!n) 匹配任何其后没有紧接指定字符串 n 的字符串。 //匹配后面字符不为10的win var str = "win7 win8 win10"; var res = str.match(/win(?!10)/g); console.log(res); //["win", "win"](?!B)[A-Z]这种写法,其实它是[A-Z]范围里,排除B的意思,前置的(?!B)只是对后面数据的一个限定,从而达到过滤匹配的效果。 var str = "abcd1234"; var res = str.match(/(?!c)[a-z]/g); console.log(res); //["a", "b", "d"]练习:从一堆图片地址中,找出符合条件的图片地址。 var arr = [ 'img/a.jpg', 'img/20181014/b.png', 'image/20181014/a.jpg', 'image/20181013/b.png', '20181013/c.png' ];匹配img开头的图片地址/^img.*(jpg|png|gif)$/g匹配不以image开头的图片地址/^(?!image).*(jpg|png|gif)$/第7章 String 对象的正则方法1、match方法stringObj.match(regex)在字符串中检索匹配正则表达式regex的子串;如果匹配,返回包含匹配结果的一个数组;不匹配返回null。正则表达式regex中不带全局修饰符g,则只匹配一次。正则表达式regex中带全局修饰符g,则匹配所有结果。 var str = "1234@qq.com"; var res = str.match(/[@\.]/g); // .要加转义 console.log(res); //["@", "."]2、replace方法stringObj.replace(regex, replacement)在字符串中检索匹配正则表达式regex的子串,并替换为指定的字符串replacement;返回替换之后的新字符串。正则表达式regex中不带全局修饰符g,则只匹配一次并替换一次。正则表达式regex中带全局修饰符g,则匹配所有结果并替换所有结果。替换的时候,使用"$1"表示匹配的第一个子表达式:用$2表示第二个子表达式,以此类推。3、search方法stringObj.search(regex)在字符串中搜索符合正则表达式的结果。如果找到结果返回结果的起始位置,停止向后检索,也就是说忽略全局标识符g;如果没有匹配结果,返回-1. var str = "1234@qq.com"; var res = str.search(/[@\.]/); // .要加转义 console.log(res); //44、split方法stringObj.split(regex)把一个字符串分割成字符串数组, 返回一个数组 var str = "1234@qq.com"; var res = str.split(/[@\.]/);// .要加转义 console.log(res); //["1234", "qq", "com"]手机端判断const is_mobile = /windows phone|iphone|android/gi.test(window.navigator.userAgent)
2022年10月13日
31 阅读
0 评论
3 点赞
2022-10-13
Javascript高级-ES6新语法
第0章 先谈ES5继承继承:多个子类对象可以共用父类对象的成员属性和成员方法(代码重用--重复使用);0.1 原型链继承核心原理:B.prototype = new A();能够继承A构造函数以及原型链上的所有成员。 //定义父类构造函数 function A(){ this.age = 10; } A.prototype.say = function(){ console.log(100); } //定义子类构造函数 function B(){ } //设置子类构造函数的原型对象 = 父类构造函数的实例对象 B.prototype = new A(); var b = new B(); console.log(b.age); b.say();缺点:子对象自身的constructor属性丢失了, 变成了父类构造函数0.2 冒充继承实现方式:在子类构造函数中,调用父类构造函数的(call, apply, bind)方法,使用子类构造函数的this去冒充父类构造函数的this。父类.call(子类的对象, 其他参数); //意思是让子类的对象,去代替父类中的this。 //父类构造函数 function A(){ this.age = 10; this.say = function(){ console.log(100); } } //子类构造函数 function B(){ //将B中的this,传给A,使A中的this指向B的this。 A.call(this); } var b = new B(); console.log(b.age); b.say();缺点:这种实现继承的方式,是不能继承父类原型对象上的成员 function A(){ this.age = 10; } A.prototype.say = function(){ console.log(100); } function B(){ A.call(this); } var b = new B(); console.log(b.age); b.say();//报错0.3 Object.create()继承Object.create()是IE9才开始支持的。var 新对象 = Object.create(原型对象); 该方法就是用于创建新对象并指定原型对象的。所以就可以直接使用create方法实现继承。特点:父类构造函数以及其原型链上的成员都能继承。适合场景:新的对象 没有直接对应的一个自定义构造函数不指定原型对象(不继承),参数可以为null。 即 var obj = Object.create(null); function A(){ this.age = 10; } A.prototype.say = function(){ console.log(100); } var obj = Object.create(new A()); console.log(obj.age); obj.say(); //或者 /* var obj = Object.create({ age:10, say:function(){ console.log(100); } }); console.log(obj.age); obj.say(); */ 第1章 常量ES5没有定义声明常量的方式,ES6标准中引入了新的关键字const来定义常量。<script> const PI = 3.14; console.log(PI); </script>常量必须给初始值; 常量不能在同一作用域内重新定义或赋值;<script> //常量不能在同一作用域中重新定义 const PI = 3.14; const PI = 3.1415; //报错 </script> <script> //常量不能在同一作用域中重新赋值 const PI = 3.14; PI = 3.1415; //报错 </script> <script> //不同作用域中可以声明同名常量 const PI = 3.14; console.log(PI);//3.14 function fn(){ const PI = 3.1415; console.log(PI); } fn();//3.1415 </script>第2章 块级作用域2.1 块级作用域JS中作用域有:全局作用域、函数作用域。ES6中新增了块级作用域。块作用域由 { } 包括,if语句和for语句里面的{ }就属于块作用域。(不包括函数)//注意 块级作用域中,使用var声明的变量是全局变量 { var a = 1; console.log(a);//1 } console.log(a);//1 if(true){ var b = 2; console.log(b);//2 } console.log(b);//22.2、let关键字声明块级变量ES6中增加了let关键字声明变量,声明的变量只在当前代码块中生效(块级作用域)。<script> if(true){ let i=0; console.log(i); } console.log(i);//报错 </script> <script> for(let i=0; i<=6; ++i){ console.log(i); } console.log(i);//报错 </script> 使用let声明的变量可以重新赋值,但是不能在同一作用域内重新声明<script> // let声明的变量可以重新赋值 { let a = 1; console.log(a); a = 2; console.log(a);; } </script> <script> // let声明的变量不能在同一作用域重新声明,直接报错 预解析错误 { let a = 1; console.log(a); let a = 2; console.log(a); } </script>2.3、let变量没有变量提升{ console.log(i);//报错 let i = 8; }2.4、应用:let块级变量解决i丢失的问题var arr = [3,4,5,6,7]; for(let i=0; i<arr.length; i++){ // (function(i){ setTimeout(function(){ console.log(i); //console.log(arr[i]); }, 1000); // })(i); }第3章 字符串模板(模板字面量)js中单双引号字符串,均不解析变量,需要使用+号将变量拼接在字符串中。ES6中提供了字符串模板语法,允许使用反引号(倒引号) `` 来创建字符串,里面可以包含${变量名}形式的变量占位符。 其中的变量会被解析。反引号字符串还可以换行//生成一个随机数 var num=Math.random(); //将这个数字输出到console console.log('your num is ' + num); console.log(`your num is ${num}`); var str = `hello 欢迎来到黑马大讲堂`; console.log(str);第4章 函数4.1 参数默认值ES5中定义函数时,不能指定参数的默认值。ES6中定义函数时,可以指定参数的默认值。//ES5中,只能变相实现参数默认值(函数内部加判断处理) function f1(username){ //传统的指定默认参数的方式 var username = username || 'zhangsan'; console.log('Hello ' + username); } f1();//Hello zhangsan f1('lisi');//Hello lisi //ES6中,直接给形参设置默认值 function f2(username='zhangsan'){ console.log(`Hello ${username}`); //console.log('Hello ' + username); } f2();//Hello zhangsan f2('lisi');//Hello lisi4.2 展开运算符(拆包)ES6新增了展开运算符(用三个连续的点 (...) 表示),能够将数组和字符串字面量展开为多个元素//展开数组 var arr = [1, 2, 3]; console.log(arr); // [1, 2, 3] console.log(...arr); // 1 2 3 //展开字符串 var str = "hello"; console.log(str); console.log(...str);应用:拓展参数它允许传递数组或者类数组直接做为函数的参数。//函数本来接收三个单独的参数 function f3(x,y,z){ console.log(x,y,z); } //ES6中,我们可以将一个数组以拓展参数的形式传递,它能很好地映射到每个单独的参数 var arr=[3,4,5]; f3(...arr);//输出:3 4 5 //ES5中,如果需要传递数组当参数,我们需要使用函数的apply方法 f3.apply(null,arr);//输出:3 4 5 4.3 不定参数(可变参数/剩余参数)不定参数是指,在函数中使用 命名参数 同时接收 不定数量 的 未命名参数,需要使用三个连续的点 (...) 。这是一种语法糖(在原语法上的简化用法),ES5通过函数内部的arguments对象来达到类似效果。不定参数的格式://不定参数 将多个实参放在一个数组变量中 // ...x 三个点是固定格式,x是形参变量名 function f1(...x){ console.log(x); } f1(3,4,5); //[3,4,5] function f2(m, n, ...x){ console.log(m, n, x); } f2(2,3,4,5,6); // m=2 n=3 x=[4,5,6]第5章 解构(拆包)在ES6中,可以使用解构从数组和对象提取值并赋值给独特的变量,即将数组或对象中的值,拆成一个一个变量。解构:自动解析数组或对象中的值,并赋值给指定的变量。5.1 数组解构将数组中的值,取出并赋值给多个变量 var arr = [3,4,5]; var [a, b, c] = arr; console.log(a, b, c); //还可以忽略值 需要使用,占位 var arr = [3,4,5]; var [a,, c] = arr; console.log(a, c); //函数返回值为数组,进行解构 function f5(){ return [1,2,3]; } var [a, b, c] = f5(); console.log(a, b, c);5.2 对象解构将对象中的成员值,取出并赋值给多个变量(变量名与对象成员名一致)var person = { "nickname": "老三", "age": 30, "sex": "男" }; //解构时 {}中的变量名,不能加引号 var {nickname, age, sex} = person; console.log(nickname, age, sex); //可以忽略值 直接忽略 不需要占位 var {nickname, sex} = person; console.log(nickname, sex);5.3 函数参数默认值与解构5.3.1 函数参数与解构函数参数使用解构数组或解构对象形式//1.函数形参,使用解构数组形式,调用函数时需要传递数组实参 function f1([x,y,z]){ console.log(x,y,z); } var arr = [1,2,3]; f1(arr); //相当于 /* function f1(a){ var [x,y,z] = a; console.log(x,y,z); } var arr = [1,2,3]; f1(arr); */ //2.函数形参,使用解构对象形式,调用函数时需要传递对象实参 function f2({nickname,age,sex}){ //变量名与对象成员名一致 console.log(nickname,age,sex); } var obj = {"nickname":"zhangsan", "age":40, "sex":"男"}; f2(obj); //相当于 /* function f1(a){ var {nickname, age, sex} = a; console.log(nickname,age,sex); } var obj = {"nickname":"zhangsan", "age":40, "sex":"男"}; f2(obj); */5.3.2 默认值与解构数组函数参数使用解构数组 并设置默认值<script> //1.函数参数使用解构数组,调用函数不传参数会报错 function fn([x, y, z]){ console.log(x, y, z); } fn(); //会报错 </script> <script> //2.函数参数使用解构数组,对整个数组设置默认值为空数组 function f1([x, y, z] = []){ console.log(x, y, z); } f1(); //不报错 x y z 都是 undefined //3.函数参数使用解构数组,对整个数组设置默认值,数组中每个变量对应一个默认值 function f2([x, y, z] = [1,2,3]){ console.log(x, y, z); } f2(); //不报错 x=1 y=2 z=3 f2([4,5,6]); // x=4 y=5 z=6 //4.函数参数使用解构数组,对整个数组设置默认值为空数组, 在解构数组中对每个变量设置一个默认值 function f3([x=1, y=2, z=3]=[]){ console.log(x, y, z); } f3(); //不报错 x=1 y=2 z=3 f3([4,5,6]); // x=4 y=5 z=6 </script>5.3.3 默认值与解构对象函数参数使用解构对象 并设置默认值<script> //1.函数参数使用解构对象,调用函数不传参数会报错 function fn({x, y, z}){ console.log(x, y, z); } fn(); //会报错 </script> <script> //2.函数参数使用解构对象,对整个对象设置默认值为空对象 function f1({x, y, z} = {}){ console.log(x, y, z); } f1(); //不报错 x y z 都是 undefined //3.函数参数使用解构对象,对整个对象设置默认值,对象中每个变量对应一个默认值 function f2({x, y, z} = {"x":1,"y":2,"z":3}){ console.log(x, y, z); } f2(); //不报错 x=1 y=2 z=3 f2({"x":4,"y":5,"z":6}); // x=4 y=5 z=6 //4.函数参数使用解构对象,对整个对象设置默认值为空对象, 在解构对象中对每个变量设置一个默认值 function f3({x=1, y=2, z=3}={}){ console.log(x, y, z); } f3(); //不报错 x=1 y=2 z=3 f3({"x":4,"y":5,"z":6}); // x=4 y=5 z=6 </script>第6章 简化的(增强的)对象字面量ES5中的对象字面量var person = { "nickname": "老三", "age": 30, "sex": "男", "say":function(){ return "hello"; } };6.1 成员属性//如果成员属性值,是放在变量中的值, 且属性名称与变量名称一致 var nickname = "老三"; var age = 30; var sex = "男"; // 预期 {"nickname":"老三", "age":30, "sex":"男"} //对象字面量可简写如下 var person = { nickname, age, sex }; console.log(person);6.2 成员方法成员方法 可省略 function 关键字// 预期 {"nickname":"老三", "age":30, "sex":"男", "say":function(){return "hello";}} var person = { "nickname":"老三", "age":30, "sex":"男", say(){ return "hello"; } }; console.log(person); console.log(person.say());//hello6.3 原型对象可以在对象字面量里面定义原型var person = { say(){ return "hello"; } }; var coder = { __proto__:person, coding(){ return "I'm coding"; } }; console.log( coder.say() ); console.log( coder.coding() );第7章 for of值遍历ES6新增了for...of循环语法。//遍历数组 var team = ["师父", "大师兄", "二师兄", "沙师弟", "小白龙"]; for(var v of team){ console.log(v); } //也可以遍历字符串 var str = "zhangsan"; for(var v of str){ console.log(v); } //for of不能遍历对象{} var person = {"nickname":"老三", "age":30, "sex":"男"}; for(var v of person){ //报错 console.log(v); }第8章 Symbol数据类型ES5数据类型:6种: string number boolean null undefined objectES6新增了一种数据类型:Symbol,表示独一无二的值,Symbol最大的用途是用来定义对象的唯一属性名。Symbol值通过Symbol函数生成。var symbol1 = Symbol(); var symbol2 = Symbol("Alice"); console.log(symbol1, symbol2) // 输出:Symbol() Symbol(Alice)typeof运算符用于Symbol类型值,返回symbol。console.log(typeof Symbol("Alice")) // 输出:symbolSymbol类型的值是一个独一无二的值,Symbol函数的参数只是表示对当前Symbol值的描述,因此相同参数的Symbol函数的返回值是不相等的。console.log(Symbol() == Symbol()); // 输出:false console.log(Symbol("Alice") == Symbol("Alice")); // 输出:false 应用:作为对象属性名的Symbolvar attr_name = Symbol(); var obj = { [attr_name]: "Alice" }; console.log(obj[attr_name]); var obj = { [Symbol()]:"Alice" }; console.log(obj);注:Symbol值作为对象属性名时,不能用点运算符。由于点运算符后面总是字符串,所以不会读取attr_name作为标识名所指代的那个值。使用[]方括号,里面的attr_name不带引号,表示attr_name是一个变量.第9章 类和对象1、类的定义ES6中添加了对类的支持,引入了class关键字ES6中提供的类实际上只是JS原型模式的包装。现在提供class支持后,对象的创建、继承更加直观。class类中,可以包含 构造方法、实例方法、静态方法。//类的定义 class A { //ES6中的构造方法(类的属性,定义在构造方法中) constructor(name) { this.name = name; this.age = 30; } //实例方法 say() { console.log('我是A中的实例方法say,我的名字是 '+this.name); } //静态方法(静态方法与实例方法 同名互不影响) static say(){ console.log("我是A中的静态方法say"); } } //直接调用静态方法 A.say(); //实例化类 调用实例方法 var a = new A('Tom'); a.say(); //类也有原型对象 console.log(A.prototype); console.log(a.__proto__)注意:1.class类中不能直接定义属性,只能定义方法,方法之间不需要也不能使用逗号隔开2.类只能先定义,再使用,没有提升效果。3.静态方法只能通过类名直接调用,实例方法只能将实例化成对象后调用。2、类的继承注:父类有构造函数,子类构造函数中,需要调用super() 实现父类的构造函数,否则报错。//类的继承 //父类A class A { //ES6中的构造方法 constructor(name) { this.name = name; this.age = 30; } //实例方法 say() { console.log('我是A中的实例方法say,我的名字是 '+this.name); } //静态方法 static say(){ console.log("我是A中的静态方法say"); } } //子类B class B extends A { //构造方法 constructor(name) { //使用函数形式的super(), 直接调用父类构造方法 //只要子类写了构造方法,就必须调用super(),且必须在使用this之前 super(name); } //实例方法 //子类方法 会覆盖父类同名方法 say() { //子类方法中,可以使用super.方法() 调用父类的非构造方法 //当前方法是实例方法,则调用父类的实例方法 //当前方法是静态方法,则调用父类的静态方法 //super.say(); console.log('我是B中的实例方法say,我的名字是 '+this.name); } static coding() { console.log('我是B中的静态方法coding'); } } //调用静态方法 B.say(); //A的静态方法 B.coding();//B自己的静态方法 //调用实例方法 var b = new B('Lucy'); b.say();//B中的实例方法say //如果B中没有,才调用A的实例方法say b.coding();//报错,B和A中都没有实例方法coding (只有静态方法)第10章 箭头函数ES6可以使用“箭头”(=>)定义函数,注意是函数,不要使用这种方式定义类(构造器)。10.1 语法1.具有一个参数并直接返回的函数var f1 = a=>a; //相当于 var f1 = function(a){ return a;}; console.log(f1('hello'));//'hello'2.没有参数的需要用在箭头前加上小括号var f2 = () => '来了老弟'; console.log(f2());3.多个参数需要用到小括号,参数间逗号间隔var f3 = (a, b) => a+b; console.log(f3(3,4));//74.函数体多条语句需要用到大括号var f4 = (a, b) => { console.log('来了老弟'); return a+b; } console.log(f4(5,6));//115.返回对象时需要用小括号包起来,因为大括号被占用解释为代码块var f5 = () => { return ({"name":"老弟", "age":40}); } //var f5 = () => ({"name":"老弟", "age":40}); console.log(f5());6.直接作为事件处理函数<input type="button" value="点击" id="btn"> <script> document.getElementById('btn').onclick = evt=>{ console.log(evt);//evt 事件对象 } </script>7.赋值为对象的方法var obj = {}; obj.say = ()=>{return "hello,我是obj的say方法";} console.log(obj.say());8.作为回调函数var f6 = (f)=>{ console.log(f(100)); }; // f6(a=>a); var f7 = a=>a; f6(f7);10.2 注意点typeof 判断箭头函数 结果为functionvar f1 = a=>a; console.log(typeof f1);//'function'instanceof判断是否Function的实例,结果为truevar f1 = a=>a; console.log(f1 instanceof Function);//true箭头函数不绑定this, 内外this指向固定不变这个很有用,再不用写me,self,_this了,或者bind。var obj = { say:function(){ //非箭头函数 var _this = this; var f1 = function(){ console.log(_this);//obj console.log(this);//window }; f1(); //箭头函数 var f2 = ()=>{ console.log(this); }; f2(); } }; obj.say();箭头函数不能做构造函数,不能用new实例化,也没有prototype属性var Person = ()=>{}; console.log(Person.prototype);//undefined var p = new Person();//报错不能使用argumentsvar f1 = ()=>{ console.log(arguments); }; f1(); //报错6.箭头函数也支持默认参数、剩余参数、解构var f1 = (x=1,y)=>{ console.log(x, y); //3 4 }; f1(3,4); var f2 = (...x)=>{ console.log(x); //[3,4] }; f2(3,4); var f3 = ([x,y]=[])=>{ console.log(x, y); //3 4 }; f3([3,4]);
2022年10月13日
17 阅读
0 评论
1 点赞
2022-10-13
Javascript高级-语言特性
第0章 课程回顾0.1 JS语法基础-复习变量:变量的声明及命名规则数据类型:数值、字符串、布尔、undefined、null、对象运算符:+ - * \ == != < > ?: && ||流程控制结构: if else 、 switch case 、while 、for ;数组及函数:声明、访问、调用……作用域:全局作用域、局部作用域、作用链对象:声明、调用、属性、方法内置对象:Math、Date、Array、StringJS代码执行流程0.2 JS-web-api-复习API:浏览器对象模型定时器(setTimeout()、setInterval())location对象、history对象、navigator对象事件:鼠标、键盘、表单、绑定、事件传播、事件对象(事件发生瞬间的一切信息,都包含在事件对象中)DOM:文档对象模型DOM对象、节点对象、节点属性、获取节点元素、节点元素的增删改查createElement、childNodes、removeChild、parentNode、nextSibling、firstChild、innerHTML……0.3 JS高级课程介绍0.4 案例:交换变量的值临时变量、加减运算、数组方式、对象方式0.5 数组遍历for()循环 for in索引遍历 数组.forEach方法 for of 值遍历(ES6)0.6 数据在内存中的存储内存地址分区:基本类型:非对象 string number boolean undefined null 数据直接存储在栈区var a = 1;变量栈区堆区a1 var a = 1; var b = a;变量栈区堆区a1 b1 var a = 1; var b = a; b = 2;变量栈区堆区a1 b2 引用类型:对象(array object function) 堆区存数据, 栈区存数据在堆区的地址var obj = {"age":40, "sex":"男"};变量栈区堆区堆区地址obj00000001(堆区地址){"age":40, "sex":"男"}00000001var obj = {"age":40, "sex":"男"}; var obj2 = obj;变量栈区堆区堆区地址obj00000001{"age":40, "sex":"男"}00000001obj200000001 var obj = {"age":40, "sex":"男"}; var obj2 = obj; obj2.age = 30;变量栈区堆区堆区地址obj00000001{"age":30, "sex":"男"}00000001obj200000001 传值方式:值传递 :基本数据类型,直接将变量放在栈区的值,复制一份,传给另外一个变量。引用传递:对象类型,将变量放在堆区的值的地址,传给另外一个变量。js中,对象(object , array, function), 传递过程中,都使用引用传递。第1章 JS面向对象编程学习目标初步理解对象是什么及面向对象编程的概念能够自己创建一个对象1.1 面向对象介绍什么是对象?Everything is object (万物皆对象), JS语言中将一切都视为 对象对象是对概念的具体化体现:一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。编程中对象是一个容器,封装了属性(property)和方法(method)属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为animal对象,使用“属性”记录具体是那一种动物,使用“方法”表示动物的某种行为(奔跑、捕猎、休息等等)。也可以将其简单理解为:数据集或功能集。ECMAScript 把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。1.2 面向对象编程面向过程:以前写js代码,都是面向过程。面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性。面向对象编程 —— Object Oriented Programming,简称 OOP ,是一种编程开发思想。它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。典型问题:将大象关进冰箱分几步面向过程式:开门(冰箱)放进(冰箱,大象)关门(冰箱)面向对象式:两个对象:大象、冰箱冰箱.开门()冰箱.放进(大象)冰箱.关门()在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目。面向对象与面向过程:面向过程就是亲力亲为,事无巨细,面面俱到,步步紧跟,有条不紊面向对象就是找一个对象,指挥得结果面向对象将执行者转变成指挥者面向对象不是面向过程的替代,而是面向过程的封装面向对象的特性:封装性:对象中的属性、方法,对外提供一组方法(操作数据的接口),使用时无需关心内部具体实现。继承性:将同一类对象,公共的属性方法,提取到一个单独公共对象中,具体的子对象可以使用这个公共对象中的成员[多态性]:动物的叫声为例,同一类的对象,有相同的方法(动物会叫), 但是每个具体的对象,方法实现的效果不一样(每个动物叫声不一样)扩展阅读:维基百科 - 面向对象程序设计知乎:如何用一句话说明什么是面向对象思想?知乎:什么是面向对象编程思想?1.3 创建对象JavaScript 语言(ES5)的对象体系,不基于“类” 创建对象,是基于构造函数(constructor)和原型链(prototype)。简单方式创建对象我们可以直接通过 new Object() 创建:var person = new Object() person.name = 'Jack' person.age = 18 person.sayName = function () { console.log(this.name) }字面量方式创建对象每次创建通过 new Object() 比较麻烦,所以可以通过它的简写形式对象字面量来创建:var person = { name: 'Jack', age: 18, sayName: function () { console.log(this.name) } }对于上面的写法固然没有问题,但是假如我们要生成两个 person 实例对象呢?var person1 = { name: 'Jack', age: 18, sayName: function () { console.log(this.name) } } var person2 = { name: 'Mike', age: 16, sayName: function () { console.log(this.name) } }通过上面的代码我们不难看出,这样写的代码太过冗余,重复性太高。简单方式的改进:工厂函数我们可以写一个函数,解决代码重复问题:function createPerson (name, age) { return { name: name, age: age, sayName: function () { console.log(this.name) } } }然后生成实例对象:var p1 = createPerson('Jack', 18) var p2 = createPerson('Mike', 18)这样封装确实爽多了,通过工厂模式我们解决了创建多个相似对象代码冗余的问题,但是这依然没有脱离 使用 字面量方式创建对象 的本质;第2章 构造函数学习目标构造函数语法分析构造函数构造函数和实例对象的关系实例的 constructor 属性instanceof 操作符普通函数调用和构造函数调用的区别构造函数的返回值构造函数的问题2.1 构造函数JavaScript 语言使用构造函数作为对象的模板。所谓 ”构造函数”,就是一个普通的函数,只不过我们专门用它来生成对象(new 构造函数),这样使用的函数,就是构造函数;它提供模板,描述对象的基本结构。一个构造函数,可以生成多个对象,这些对象都有相同的结构。function Person (name, age) { this.name = name this.age = age this.sayName = function () { console.log(this.name) } } var p1 = new Person('Jack', 18) p1.sayName() // => Jack var p2 = new Person('Mike', 23) p2.sayName() // => Mike解析 构造函数代码 的执行在上面的示例中,Person() 函数取代了 createPerson() 函数,但是实现效果是一样的。这是为什么呢?我们注意到,Person() 中的代码与 createPerson() 有以下几点不同之处:没有显式的创建对象(没有使用字面量)直接将属性和方法赋给了 this没有 return 语句函数名使用的是大写的 Person而要创建 Person 实例,则必须使用 new 操作符。以这种方式调用构造函数会经历以下 5 个步骤:创建一个空对象,作为将要返回的对象实例。将这个空对象的原型,指向构造函数的prototype属性。先记住,后面讲将这个空对象赋值给函数内部的this关键字。执行构造函数内部的代码。返回新对象function Person (name, age) { // 当使用 new 操作符调用 Person() 的时候,实际上这里会先创建一个对象 // 然后让内部的 this 指向新创建的对象 // 接下来所有针对 this 的操作实际上操作的就是刚创建的这个对象 this.name = name this.age = age this.sayName = function () { console.log(this.name) } // 在函数的结尾处会将 this 返回,也就是这个新对象 }构造函数和实例对象的关系构造函数是根据具体的事物抽象出来的抽象模板实例对象是根据抽象的构造函数模板得到的具体实例对象实例对象由构造函数而来,一个构造函数可以生成很多具体的实例对象,而每个实例对象都是独一无二的;每个对象都有一个 constructor 属性,该属性指向创建该实例的构造函数反推出来,每一个对象都有其构造函数console.log(p1.constructor === Person) // => true console.log(p2.constructor === Person) // => true console.log(p1.constructor === p2.constructor) // => true因此,我们可以通过实例对象的 constructor 属性判断实例和构造函数之间的关系注意:这种方式不严谨,推荐使用 instanceof 操作符,后面学原型会解释为什么console.log(p1 instanceof Person) // => true console.log(p2 instanceof Person) // => trueconstructor 既可以判断也可以获取instanceof 只能用于判断2.2 构造函数存在的问题以构造函数为模板,创建对象,对象的属性和方法都可以在构造函数内部定义;function Cat(name, color) { this.name = name; this.color = color; this.say = function () { console.log('hello'+this.name,this.color); }; } var cat1 = new Cat('猫', '白色'); var cat2 = new Cat('猫', '黑色'); cat1.say(); cat2.say();在该示例中,从表面上看好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象, name 和 say 都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。对于这种问题我们可以把需要共享的函数定义到构造函数外部:function say(){ console.log('hello'+this.name,this.color); } function Cat(name, color) { this.name = name; this.color = color; this.say = say; } var cat1 = new Cat('猫', '白色'); var cat2 = new Cat('猫', '黑色'); cat1.say(); cat2.say();这样确实可以了,但是如果有多个需要共享的函数的话就会造成全局变量(函数名)冲突的问题。你肯定想到了可以把多个函数放到一个对象中用来避免全局变量(函数名)冲突的问题:var s = { sayhello:function (){ console.log('hello'+this.name,this.color); }, saycolor:function(){ console.log('hello'+this.color); } } function Cat(name, color) { this.name = name; this.color = color; this.sayhello = s.sayhello; this.saycolor = s.saycolor; } var cat1 = new Cat('猫', '白色'); var cat2 = new Cat('猫', '黑色'); cat1.sayhello(); cat2.saycolor();至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。但是代码看起来还是那么的格格不入,那有没有更好的方式呢?小结构造函数语法分析构造函数构造函数和实例对象的关系实例的 constructor 属性instanceof 操作符构造函数的问题第3章 原型学习目标使用 prototype 原型对象解决构造函数的问题理解什么是原型(原型对象)构造函数、prototype 原型对象、实例对象 三者之间的关系实例对象读写原型对象属性成员搜索原则:原型链原型对象的简写形式原生对象的原型原型对象的问题及使用建议3.1 构造函数的 prototype属性JavaScript 的每个对象都继承另一个父级对象,父级对象称为 原型 (prototype)对象。原型也是一个对象,原型对象上的所有属性和方法,都能被子对象 (派生对象) 共享通过构造函数生成实例对象时,会自动为实例对象分配原型对象。而每一个构造函数都有一个prototype属性,这个属性就是实例对象的原型对象。null没有自己的原型对象。这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在构造函数的 prototype 属性上,也就是实例对象的原型对象上。function Cat(color) { this.color = color; } Cat.prototype.name = "猫"; Cat.prototype.sayhello = function(){ console.log('hello'+this.name,this.color); } Cat.prototype.saycolor = function (){ console.log('hello'+this.color); } var cat1 = new Cat('白色'); var cat2 = new Cat('黑色'); cat1.sayhello(); cat2.saycolor();这时所有实例的 name 属性和 sayhello() 、saycolor 方法,其实都是同一个内存地址,指向构造函数的 prototype 属性,因此就提高了运行效率节省了内存空间。3.2 构造函数、实例、原型三者之间的关系构造函数的prototyp属性,就是由这个构造函数new出来的所有实例对象的 原型对象前面已经讲过,每个对象都有一个 constructor 属性,该属性指向创建该实例的构造函数对象.\_proto_ (两边都是两个下划线):获取对象的原型对象;console.log(cat1.__proto__ == Cat.prototype); // true注意:ES6标准规定,\__proto__属性只有浏览器环境下才需要部署,其他环境可以不部署,因此不建议使用3.3 原型对象的获取及修改上节可以看到,想要获取一个实例对象的原型对象,有两种方式:1:通过实例对象的构造函数的prototype属性获取: 实例对象.constructor.prototype2:通过实例对象的 \_proto_ 属性获取: 实例对象.__proto__而这两种方式,我们都不建议使用:obj.constructor.prototype在手动改变原型对象时,可能会失效。function P() {}; var p1 = new P(); function C() {}; // 修改构造函数的prototype属性的值为p1 C.prototype = p1; //也就是说,此后所有有C构造函数得到的对象的原型对象都是p1; var c1 = new C(); console.log(c1.constructor.prototype === p1) // false推荐设置获取实例对象的原型的方式:Object.getPrototypeOf(实例对象) 方法返回一个对象的原型对象。这是获取原型对象的标准方法。function Cat(name, color) { this.name = name; } var cat1 = new Cat('猫'); //获取cat1对象的原型对象 var s = Object.getPrototypeOf(cat1); console.log(s);Object.setPrototypeOf(实例对象,原型对象) 为现有对象设置原型对象第一个是实例对象,第二个是要设置成为实例对象的原型对象的对象这是设置原型对象的标准方法。function Cat(name) { this.name = name; } var ob = {p:'波斯'}; var cat1 = new Cat('猫'); //设置cat1的原型对象为ob Object.setPrototypeOf(cat1,ob); console.log(cat1.p);//cat1的原型对象中有p属性 console.log(Object.getPrototypeOf(cat1)); console.log(cat1.__proto__); //注意:如果对象的原型被改变,不会影响构造函数获取的原型的结果 console.log(Cat.prototype == cat1.__proto__); //false以上的两种方法,都是在ES6新标准中添加的;重要图示3.4 原型及原型链所有对象都有原型对象;function Cat(name, color) { this.name = name; } var cat1 = new Cat('猫'); console.log(cat1.__proto__.__proto__.__proto__); 而原型对象中的属性和方法,都可以被实例对象直接使用;每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性搜索首先从对象实例本身开始如果在实例中找到了具有给定名字的属性,则返回该属性的值如果没有找到,则继续搜索原型对象,在原型对象中查找具有给定名字的属性如果在原型对象中找到了这个属性,则返回该属性的值如果还是找不到,就到原型的原型去找,依次类推。如果直到最顶层的Object.prototype还是找不到,则返回undefined。而这正是多个对象实例共享原型所保存的属性和方法的基本原理。对象的属性和方法,有可能是定义在自身内,也有可能是定义在它的原型对象上。由于原型本身也是对象,又有自己的原型,所以形成了一条 原型链(prototype chain)。3.5 更简单的原型语法我们注意到,前面例子中每添加一个属性和方法就要敲一遍 构造函数.prototype 。为减少不必要的输入,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象:function Person (name, age) { this.name = name this.age = age } Person.prototype = { type: 'human', sayHello: function () { console.log('我叫' + this.name + ',我今年' + this.age + '岁了') } }在该示例中,我们将 Person.prototype 重置到了一个新的对象。这样做的好处就是为 Person.prototype 添加成员简单了,但是也会带来一个问题,那就是原型对象丢失了 constructor 成员(构造函数)。所以,我们为了保持 constructor 的指向正确,建议的写法是:function Person (name, age) { this.name = name this.age = age } Person.prototype = { // 将这个对象的构造函数指向Person //constructor: Person, // => 手动将 constructor 指向正确的构造函数 type: 'human', sayHello: function () { console.log('我叫' + this.name + ',我今年' + this.age + '岁了') } } var p = new Person();3.6 原生对象的原型所有构造函数都有prototype属性;Object.prototypeFunction.prototypeArray.prototypeString.prototypeNumber.prototypeDate.prototype……为内置对象扩展原型方法:例:var ar = [1,5,23,15,5]; //获取数组中小于10的数 function f(){ var minarr = []; //存放返回结果的数组,里面放小于10的数 this.forEach(function(v,k){ if(v<10){ //将数组中 小于10的数,放到新的数组中 minarr.push(v); } }) return minarr; } Object.getPrototypeOf(ar).min10 = f;// Array.prototype.min10 = f; console.log(ar.min10());//[1, 5, 5] // 其他数组对象也具有相应的方法 var a = [1,2,34,7]; console.log(a.min10()); //[1, 2, 7]这种技术被称为猴子补丁,并且会破坏封装。尽管一些流行的框架(如 Prototype.js)在使用该技术,但仍然没有足够好的理由使用附加的非标准方法来混入内置原型。3.7 原型对象的问题及使用建议性能问题:在原型链上查找属性时是比较消耗资源的,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。//声明构造函数Man function Man(name){ this.name = name; this.p = function(){ console.log(this.name+'跑'); } } var m = new Man('张三'); console.log(m.hasOwnProperty('name')); // true console.log(m.hasOwnProperty('age')); //falsehasOwnProperty 是 JavaScript 中唯一处理属性并且不会遍历原型链的方法。注意:检查属性是否undefined还不够。该属性可能存在,但其值恰好设置为undefined。//声明构造函数Man function Man(name){ this.name = name; this.n = undefined; this.p = function(){ console.log(this.name+'跑'); } } var m = new Man('张三'); if(m.n == undefined){ console.log('没有n属性') } console.log(m.hasOwnProperty('name')); // true console.log(m.hasOwnProperty('name')); // true console.log(m.hasOwnProperty('n')); //true第4章 继承学习目标理解什么是继承原型继承4.1 什么是继承现实生活中的继承程序中的继承所谓的继承,其实就是在子类(子对象)能够使用父类(父对象)中的属性及方法;赋予后辈调用祖辈资源的权限,就是继承;4.2 原型链继承//声明构造函数Run function Run(){ this.p = function(){ console.log(this.name+'跑'); } } //声明构造函数Man function Man(name){ this.name = name; } //设置构造函数Man的原型为Run,实现继承 Man.prototype = new Run(); var m = new Man('张三'); m.p();但是,并不建议使用原型链继承,而且JS 中不止有原型链继承,还有其他的继承方式,后面会讲到;第5章 函数进阶学习目标函数的声明调用参数等基本语法理解作用域闭包this的指向和使用改变this指向的方法5.1 函数的声明及调用关键字声明function f1(){ console.log('f1'); }表达式声明var f2 = function(){ console.log('f2'); }这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式(Function Expression)构造函数方式声明var add = new Function( 'x', 'y', 'console.log( x + y )' ); add(1,2);上面代码与下面的代码等同;// 等同于 function add(x, y) { console.log( x + y ) } add(1,2);因此,我们只关注前两种即可,后一种只做了解,不建议使用,这种声明函数的方式非常不直观,几乎无人使用;那么,关键字声明和表达式声明 有什么区别?关键字声明必须有名字关键字声明会函数提升,在预解析阶段就已创建,声明前后都可以调用函数表达式类似于变量赋值函数表达式没有函数名字函数表达式没有变量提升,在执行阶段创建,必须在表达式执行之后才可以调用// 先调用后声明 f1(); function f1(){ console.log('f1'); } //由于“变量提升”,函数f被提升到了代码头部,也就是在调用之前已经声明了 //因此可以正常调用 而表达式方式:f(); var f = function (){}; // TypeError: undefined is not a function上面的代码等同于下面的形式。var f; f(); f = function () {};上面代码第二行,调用f的时候,f只是被声明了,还没有被赋值,等于undefined,所以会报错。因此,如果同时采用function命令和表达式声明同一个函数,最后总是采用表达式的定义。var f = function () { console.log('1'); } function f() { console.log('2'); } f() // 15.2 第一等公民JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为 第一等公民。函数作为参数(被作为参数)function eat (callback) { setTimeout(function () { console.log('吃完了') callback() }, 1000) } eat(function () { console.log('去唱歌') })函数作为返回值function f1(){ var s = 1; function f2(){ console.log(s); } return f2; } var f = f1(); f();// 1JS中一切皆对象,函数也是对象,后面还会讲到5.3 参数及返回值5.3.1 参数形参和实参// 函数内部是一个封闭的环境,可以通过参数的方式,把外部的值传递给函数内部 // 带参数的函数声明 function 函数名(形参1, 形参2, 形参...){ // 函数体 } // 带参数的函数调用 函数名(实参1, 实参2, 实参3);解释:形式参数:在声明一个函数的时候,为了函数的功能更加灵活,有些值是固定不了的,对于这些固定不了的值。我们可以给函数设置参数。这个参数没有具体的值,仅仅起到一个占位置的作用,我们通常称之为形式参数,也叫形参。实际参数:如果函数在声明时,设置了形参,那么在函数调用的时候就需要传入对应的参数,我们把传入的参数叫做实际参数,也叫实参。 function fn(a, b) { console.log(a + b); } var x = 5, y = 6; fn(x,y); //x,y实参,有具体的值。函数执行的时候会把x,y复制一份给函数内部的a和b,函数内部的值是复制的新值,无法修改外部的x,yvar f = function (one) { console.log(one); } f(1, 2, 3)由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。arguments对象 — 函数的实参参数集合var f = function (one) { console.log(arguments); console.log(arguments[0]); console.log(arguments[1]); console.log(arguments[2]); } f(1, 2, 3)5.3.2 返回值当函数执行完的时候,并不是所有时候都要把结果打印。我们期望函数给我一些反馈(比如计算的结果返回进行后续的运算),这个时候可以让函数返回一些东西。也就是返回值。函数通过return返回一个返回值//声明一个带返回值的函数 function 函数名(形参1, 形参2, 形参...){ //函数体 return 返回值; } //可以通过变量来接收这个返回值 var 变量 = 函数名(实参1, 实参2, 实参3);函数的调用结果就是返回值,因此我们可以直接对函数调用结果进行操作。返回值详解:如果函数没有显示的使用 return语句 ,那么函数有默认的返回值:undefined如果函数使用 return语句,那么跟在return后面的值,就成了函数的返回值如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined , 函数使用return语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说return后面的所有其他代码都不会再执行。return后没有任何内容,可以用来调试代码。5.3.3 递归执行代码,查看执行结果:function fn1 () { console.log(111) fn2() console.log('fn1') } function fn2 () { console.log(222) fn3() console.log('fn2') } function fn3 () { console.log(333) fn4() console.log('fn3') } function fn4 () { console.log(444) console.log('fn4') } fn1() /* ** 执行结果为: 111 222 333 444 fn4 fn3 fn2 fn1 */最简单的一句话介绍递归:函数内部自己调用自己递归案例:计算 1+2+3+......+100 的结果function sum(n){ if(n>1){ return n + sum(n-1); }else{ return 1; } } var jieguo = sum(5); console.log(jieguo);递归必须要有判断条件,不加判断会死;5.4 作用域作用域(scope)指的是变量存在的范围。在 ES5 的规范中,Javascript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域(局部作用域),变量只在函数内部。函数外部声明的变量就是全局变量(global variable),它可以在函数内部读取。var v = 1; function f() { console.log(v); } f() // 1在函数内部定义的变量,外部无法读取,称为“局部变量”(local variable)。function f(){ var v = 1; } v // ReferenceError: v is not defined注意,对于var命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量。5.4.1 作用域链只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域, 即全局作用域。凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。function f1() { var num = 123; function f2() { console.log( num ); } f2(); } var num = 456; f1();5.5 函数内部的变量声明的提升与全局作用域一样,函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。function foo() { console.log(y);//undefined var y = 'Bob'; } foo();注意: JavaScript引擎自动提升了变量y的声明,但不会提升变量y的赋值。对于上述foo()函数,JavaScript引擎看到的代码相当于:function foo() { var y; // 提升变量y的申明 var x = 'Hello, ' + y; alert(x); y = 'Bob'; }5.6 函数本身的作用域var a = 1; var x = function () { console.log(a); }; function f() { var a = 2; x(); } f() // 1 // 讨论结果为什么是 1 ?函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。总之,函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。var x = function () { console.log(a); }; function y(f) { var a = 2; f(); } y(x);// ReferenceError: a is not definedfunction foo() { var x = 1; function bar() { console.log(x); } return bar; } var x = 2; var f = foo(); f() // 15.7 闭包5.7.1 关于作用域的问题var n = 999; function f1() { console.log(n); } f1() // 999函数内部可以直接读取全局变量,函数 f1 可以读取全局变量 n。但是,在函数外部无法读取函数内部声明的变量。function f1() { var n = 99; } f1() console.log(n);有时我们却需要在函数外部访问函数内部的变量;正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。function f1() { var n = 999; var f2 = function() { console.log(n); } return f2; } var f = f1(); f();上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是JavaScript语言特有的”链式作用域”结构(chain scope),子级会一层一层地向上寻找所有父级的变量。所以,父级的所有变量,对子级都是可见的,反之则不成立。既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!闭包就是函数f2,即能够读取其他函数内部变量的函数。由于在JavaScript语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁;闭包(closure)是 Javascript 语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。理解闭包,首先必须理解变量作用域。5.7.2 关于JS垃圾回收机制的问题function f1() { var n = 99; console.log(++n); } f1(); //100 f1(); //100当我们在函数内部引入一个变量或函数时,系统都会开辟一块内存空间;还会将这块内存的引用计数器进行初始化,初始化值为0,如果外部有全局变量或程序引用了这块空间,则引用计数器会自动进行+1操作,当函数执行完毕后,如果变量计数器为0,系统会运行垃圾回收,将函数运行产生的数据销毁;如计数器不是 0 ,则不会清除数据;这个过程就称之为 "JS的垃圾回收机制" ;如果将上节的代码,改为闭包形式:function f1() { var n = 99; function f2(){ console.log(++n); } return f2; } var f = f1(); f(); //100 f(); //101运行代码发现,函数调用一次,其变量 n 变化一次;因 函数f1被调用时,返回的结果是f2函数体,也就是说,f2函数被当作值返回给f1的调用者,但是f2函数并没有在此时被调用执行,所以整个 f1 函数体,无法判断子函数f2会对其产生何种影响,无法判断 变量n是否会被使用;即使f1函数被调用结束,整个f1函数始终保留在内存中,不会被垃圾回收机制回收;闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在;注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。闭包小案例:缓存随机数需求:获取随机数,随机数一旦生成,会在程序中多次使用,所以,在多次使用中不变;function f1(){ var num = parseInt(Math.random()*100)+1; return function (){ console.log(num); } } var f2 = f1(); f2(); f2(); f2();闭包点赞案例:<ul> <li><img src="images/ly.jpg" alt=""><br/><input type="button" value="赞(1)"></li> <li><img src="images/lyml.jpg" alt=""><br/><input type="button" value="赞(1)"></li> <li><img src="images/fj.jpg" alt=""><br/><input type="button" value="赞(1)"></li> <li><img src="images/bd.jpg" alt=""><br/><input type="button" value="赞(1)"></li> </ul> <script> function f1() { var value=1; return function () { this.value="赞("+(++value)+")"; } } //获取所有的按钮 var btnObjs=document.getElementsByTagName("input"); for(var i=0;i<btnObjs.length;i++){ var ff=f1(); //将 闭包函数 绑定给事件 btnObjs[i].onclick=ff; } </script>思考以下两段代码的运行结果:var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { return function () { console.log(this.name); }; } }; object.getNameFunc()() var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { var that = this; return function () { console.log(that.name); }; } }; object.getNameFunc()(); 通过分析代码得知,其中的 this 是关键,那么this到底是什么?5.8 this 到底是谁5.8.1 this的不同指向this关键字是一个非常重要的语法点。毫不夸张地说,不理解它的含义,大部分开发任务都无法完成。记住一点:this不管在什么地方使用:它永远指向一个对象。下面是一个实际的例子。var person = { name: '张三', describe: function () { console.log('姓名:'+ this.name); } }; person.describe() // "姓名:张三"上面代码中,this.name表示name属性所在的那个对象。由于this.name在describe方法中调用,而describe方法所在的当前对象是person,因此this指向person,this.name就是person.name。由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即this的指向是可变的。var A = { name: '张三', describe: function () { console.log('姓名:'+ this.name); } }; var B = { name: '李四' }; B.describe = A.describe; B.describe() // "姓名:李四"上面代码中,A.describe属性被赋给B,于是B.describe就表示describe方法所在的当前对象B,所以this.name就指向B.name。稍稍重构这个例子,this的动态指向就能看得更清楚。function f() { console.log('姓名:'+ this.name); } var A = { name: '张三', describe: f }; var B = { name: '李四', describe: f }; A.describe() // "姓名:张三" B.describe() // "姓名:李四"上面代码中,函数f内部使用了this关键字,随着f所在的对象不同,this的指向也不同。只要函数被赋给另一个变量,this的指向就会变。var A = { name: '张三', describe: function () { console.log('姓名:'+ this.name); } }; var name = '李四'; var f = A.describe; f() // "姓名:李四"上面代码中,A.describe被赋值给变量f,的内部this就会指向f运行时所在的对象(本例是顶层对象)。案例:判断数值合法性<input type="text" name="age" size=3 onChange="validate(this, 10, 20);"> <script> function validate(obj, x, y){ if ((obj.value < x) || (obj.value > y)){ console.log('合法'); }else{ console.log('不合法'); } } </script>上面代码是一个文本输入框,每当用户输入一个值,鼠标离开焦点就会触发onChange事件,并执行validate回调函数,验证这个值是否在指定范围。浏览器会向回调函数传入当前对象,因此this就代表传入当前对象(即文本框),就然后可以从this.value上面读到用户输入的值。JavaScript语言之中,一切皆对象,运行环境也是对象,所以函数都是在某个对象下运行的,this就是函数运行时所在的对象(环境)。这本来并不会让我们糊涂,但是JavaScript支持运行环境动态切换,也就是说,this的指向是动态的,没有办法事先确定到底指向哪个对象,这才是最初初学者感到困惑的地方。5.8.2 使用场合(1)全局环境全局环境使用this,它指的就是顶层对象window。this === window // true function f() { console.log(this === window); } f() // true(2)构造函数构造函数中的this,指的是实例对象。function F(){ this.hello=function(){ console.log('Hello'+this.name); } } var f1 = new F(); f1.name = '张三'; f1.hello(); var f2 = new F(); f2.name = '刘能'; f2.hello();(3)对象的方法方法在哪个对象下,this就指向哪个对象。var o1 = { s1:'123', f1:function (){ console.log(this.s1) } } var o2 = { s1:'456', f1:o1.f1 } o2.f1();5.8.3 使用this时的注意事项(1) 避免包含多层thisvar o = { f1: function () { console.log(this); var f2 = function () { console.log(this); } f2(); } } o.f1() // Object // Window 如果要在内层函数中使用外层的this指向,一般的做法是:var o = { f1: function () { console.log(this); var that = this; var f2 = function () { console.log(that); } f2(); } } o.f1() // Object // Object (2)不在循环数组中使用thisvar ar = ['a','b','c']; ar.forEach(function(v,k,ar){ console.log(this[k]) })this的动态切换,固然为JavaScript创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this固定下来,避免出现意想不到的情况;JavaScript提供了call,apply,bind这三个方法,来切换/固定this的指向。5.9 call()方法、apply()方法、bind()方法call()方法var lisi = {names:'lisi'}; var zs = {names:'zhangsan'}; function f(age){ console.log(this.names); console.log(age); } f(23);//undefined f.call(zs,32);//zhangsancall方法使用的语法规则函数名称.call(obj,arg1,arg2...argN);参数说明:obj:函数内this要指向的对象,arg1,arg2...argN :参数列表,参数与参数之间使用一个逗号隔开apply()方法函数名称.apply(obj,[arg1,arg2...,argN])参数说明:obj :this要指向的对象[arg1,arg2...argN] : 参数列表,但是要求格式为数组var lisi = {name:'lisi'}; var zs = {name:'zhangsan'}; function f(age,sex){ console.log(this.name+age+sex); } f.apply(zs,[23,'nan']);bind()方法bind方法用于将创建一个新的函数,且将新函数中的this绑定到具体的某个对象上function foo() { console.log(this.a); } var obj2 = { a: 2, }; // 创建新函数,并将新函数中的this固定的指向obj2对象; var new_foo = foo.bind(obj2); new_foo(); //2 foo(); //undefined第6章 再谈 面向对象学习目标:了解ES6中新的对象语法正确使用继承6.1 对象6.1.1 谁说JS没有类在JS中,想要获取一个对象,有多种方式:var o1 = {} var o2 = new Object()自定义构造函数方式function Point(x, y) { this.x = x; this.y = y; this.toString = function () { return this.x + ', ' + this.y; }; } var p = new Point(1, 2); console.log(p.toString());但是上面这种使用构造函数获取对象的写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样。//定义类 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return this.x + ', ' + this.y ; } } var p = new Point(1, 2); console.log(p.toString());上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法(后面还会讲到),而this关键字则代表实例对象。也就是说,ES5 的构造函数Point,对应 ES6 的类Point。Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。ES6 的类,完全可以看作构造函数的另一种写法。使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。类同样也有prototype属性,而属性的值依然是实例对象的原型对象;class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return this.x + ', ' + this.y ; } } Point.prototype.toValue = function(){ console.log('123'); } var p = new Point(1, 2); p.toValue(); console.log(p.toString());6.1.2 constructor 方法constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。class Point { } // 等同于 class Point { constructor() {} }类必须使用new 进行实例化调用,否则会报错。而类一旦实例化,constructor()方法就会被执行,就像 人一出生就会哭一样;constructor方法默认返回实例对象(即this);但是返回值完全可以指定返回另外一个对象;var o1 = { f1:function(){ console.log('f1'); } } class Point{ constructor (){ return o1; } f2(){ console.log('f2'); } } var p = new Point(); p.f1(); // f1 p.f2(); //Uncaught TypeError: p.f2 is not a functionconstructor方法默认返回值尽量不要修改,一旦修改,我们获取的对象将脱离其原型;6.1.3 变量提升我们知道,在JS中,不管是全局变量还是局部变量都存在变量提升的特性,函数也有提升的特性,也可以先调用后声明,构造函数也一样;如下面的代码,完全没问题:var p = new Point(); p.f2(); function Point(){ this.f2 = function(){ console.log('f2'); } }但是,需要注意: 类不存在变量提升(hoist),这一点与 ES5 完全不同。new Man(); class Man{} // Man is not defined注意,class只是在原有面向对象的基础上新加的关键字而已,本质上依然没有改变JS面向对象方式;6.2 再谈继承6.2.1 原型链继承的问题//声明构造函数Run function Run(){ this.p = function(){ console.log(this.name+'跑'); }} //声明构造函数Man function Man(name){ this.name = name; } //设置构造函数Man的原型为Run,实现继承 Man.prototype = new Run(); var m = new Man('张三'); m.p(); // 由构造函数获取原型 console.log(Man.prototype); // 函数对象 Run //标准方法获取对象的原型 console.log(Object.getPrototypeOf(m)); // 函数对象 Run //获取对象的构造函数 console.log(m.constructor); // Run 函数运行上面的代码,我们发现对象 m 本来是通过构造函数Man 得到的,可是,m 对象丢失了构造函数,并且原型链继承的方式,打破了原型链的完整性,不建议使用;这个问题,在3.5章节也提到过,想解决也很简单,只要在父级中手动指定子级正确的构造函数即可:修改上面的代码:6.2.2 冒充方式的继承前面我们在学习JS面向中的面向对象编程时,谈到了继承;所谓的继承,其实就是在子类(子对象)能够使用父类(父对象)中的属性及方法;function f1(){ this.color = '黑色'; this.h = function(){ console.log(this.color+this.sex); } } function F2(){ this.sex = '铝'; this.fn = f1; } var b = new F2(); b.fn(); b.h();运行以上代码可知,由构造函数获取的对象 b可以调用函数f1中的属性及方法;有运行结果可知,f1 函数中的this 实际指向了对象b ,对象b 实际上已经继承了f1这种方式称为 对象冒充 方式继承,ES3之前的代码中经常会被使用,但是现在基本不使用了;为什么不使用了呢?还是要回到上面的代码,本质上讲,我们只是改变了函数f1 中this的指向,f1中的this指向谁,谁就会继承f1;而call和apply就是专门用来改变 函数中this指向的;call或apply 实现继承function Run(){ this.p = function(){ console.log('ppp'); } } function Man(){ //将Run函数内部的this指向Man的实例化对象; Run.call(this); } var m = new Man(); m.p(); //获取对象的构造函数 console.log(m.constructor); // Man 函数 // 由构造函数获取原型 console.log(Man.prototype); // 函数对象 Man //标准方法获取对象的原型 console.log(Object.getPrototypeOf(m)); // 函数对象 Mancall或apply 实现的继承依然是使用对象冒充方式实现, 此方式即实现了继承的功能,同时也不再出现原型继承中出现的问题;6.2.4 Object.create() 创建实例对象及原型继承构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.create()方法。var person1 = { name: '张三', age: 38, greeting: function() { console.log('Hi! I\'m ' + this.name + '.'); } }; var person2 = Object.create(person1); person2.name // 张三 person2.greeting() // Hi! I'm 张三. console.log(Object.getPrototypeOf(person2) == person1); //true上面代码中,对象person1是person2的模板,后者继承了前者所有的属性和方法;Object.create 的本质就是创建对象;var obj1 = Object.create({}); var obj2 = Object.create(Object.prototype); var obj3 = new Object(); //下面三种方式生成的新对象是等价的。如果想要生成一个不继承任何属性(比如没有toString和valueOf方法)的对象,可以将Object.create的参数设为null。var obj = Object.create(null); obj.valueOf() // TypeError: Object [object Object] has no method 'valueOf'使用Object.create方法的时候,必须提供对象原型,即参数不能为空,或者不是对象,否则会报错。6.2.5 Class 的继承class Run{ p(){ console.log('ppp'); } } class Man extends Run{ } var m = new Man(); m.p();Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。ES5 的继承,实质是先创造子类的实例对象,然后再将父类的方法添加到上面(Parent.call(this))。ES6 的继承机制完全不同,是先创造父类的实例对象,然后再用子类的构造函数修改。class Run{ p(){ console.log('ppp'); } } class Man extends Run{ // 显式调用构造方法 constructor(){} } var m = new Man(); m.p(); // Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived上面代码中,Man继承了父类Run,但是子类并没有先实例化Run,导致新建实例时报错。class Run{ p(){ console.log('ppp'); } } class Man extends Run{ constructor(){ // 调用父类实例(实现父类构造方法) super(); } } var m = new Man(); m.p();第7章 综合案例整体思路:先玩几次,思考大概的实现思路;7.1 黑白块1:创建基本的静态页面;2:让div动起来3:动态创建Div4:动起来后,填补缺失的div5:随机创建黑块6:绑定点击事件7:点击判断输赢8:游戏结束后的限制处理9:黑块触底的处理10:加分11:加速注意变量作用域和this指向的问题insertBefore、firstChild、getComputedStyle、appendChild、createElement、Math.random、Math.floor<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> #cont { margin-top:100px; width: 400px; height: 400px; border-top:1px solid blue; position: relative; /*隐藏顶部*/ overflow: hidden; } #main { width: 400px; height: 400px; /*去掉红框*/ /* border:1px solid red; */ position: relative; top:-100px; } .row { height: 100px; } .row div { width: 98px; height: 98px; border:1px solid gray; float: left; } .black { background: black; } </style> </head> <body> <input type="text" id="fen" value="0" disabled> <div id="cont"> <div id="main"></div> </div> </body> <script> function Youxi(){ // 获取main节点对象 this.main = document.getElementById('main'); this.interval = ''; // 定时器 this.over = false; // 有是否结束的标志 this.sudu = 1; // 初始化下降速度 // 创建DIV的方法 this.cdiv = function(classNames){ // 创建一个div节点对象 var div = document.createElement('div'); // 根据传入的值,创建不同class属性的div if(classNames){ div.className = classNames; } return div; } //一次生成一行div this.crow = function(init){ var row = this.cdiv('row'); // 获取0-3的随机数 var k = Math.floor(Math.random()*4) // 每行div根据随机数,随机设置一个黑块 for(var i=0;i<4;i++){ // 随机出现黑块 if(i==k){ row.appendChild(this.cdiv('black')); }else{ row.appendChild(this.cdiv()); } } return row; } // 初始化运行 this.init = function(){ // 循环创建4行,并添加到main中 for(var i = 0;i<4;i++){ var row = this.crow(); this.main.appendChild(row); } // 绑定点击事件 this.clicks(); // 设置定时器,使DIV动起来 this.interval = window.setInterval('start.move()' , 15); } // 绑定点击事件 this.clicks = function(){ // 因为在其他作用域中要使用本对象, // 防止this指向冲突,将this赋值给一个新的变量,在其他作用域中使用新的变量代替this var that = this; // 为整个main绑定点击事件 this.main.onclick = function(ev){ // 通过事件对象,获取具体点击的节点 var focus = ev.target; // 如果游戏已经结束了 if(that.over){ alert('别挣扎了,游戏已经结束了!'); } // 如果点击的元素有值为black的class属性, // 证明用户点击的是黑色块 else if(focus.className == 'black'){ // 获取文本框节点对象 var score = document.getElementById('fen'); // 将文本框的值获取并加1后重新复制 var sc = parseInt(score.value)+1; score.value = sc; // 将黑块变白 focus.className = ''; // 如果此行被点击过,给这一行发一个'同行证' focus.parentNode.pass = true; // 得分每增加5,下降速度提高0.5个像素点 if(sc%5 == 0){ that.sudu += 0.5; } }else{ // 点击的不是黑块,结束游戏 window.clearInterval(that.interval); // 游戏已经结束了 that.over = true; alert('游戏已结束') } } } // 每调用一次 main 的top值加2像素,main就会向下移动2像素 // 我们只需要不断调用move,就会让main不断下降 this.move = function(){ // 获取top值 var t = getComputedStyle(this.main, null)['top']; var tops = parseInt(t); // 如果tops大于1,证明一行下降结束 if(tops>1){ // 如果此行没有通行证,游戏结束 if(this.main.lastChild.pass==undefined){ window.clearInterval(this.interval); // 游戏已经结束了 this.over = true; alert('游戏已结束') }else{ // 如果有通行证 // 如果大于5行,删除最后一行 if(this.main.children.length>=5) { this.main.removeChild(this.main.lastChild); } } // 下降结束一行,则在最顶部增加一行,完成下降的连续性 var row = this.crow(); this.main.insertBefore(row,this.main.firstChild); // 并重新隐藏新加的一行 this.main.style.top = '-100px'; }else{ // 定时器每调用一次,top 值修改一次 // 完成下降动作 this.main.style.top = tops + this.sudu +'px'; } } } var start = new Youxi(); start.init(); </script> </html>7.2 贪吃蛇1、分析小游戏中共有三个对象。一个是地图(Map)、一个是食物(Food)、一个是蛇(Snake)。以面向对象的方式来开发,应该创建三个对象。每个对象的特点:地图Map: 宽width 高height 背景颜色backgroundColor 显示方法:show 地图属性:保存生成的地图(后续需要向地图中添加食物和小蛇)食物Food: 宽width 高height 背景颜色backgroundColor x轴坐标(根据地图宽/食物宽):0~29(每个坐标实际像素是20px) y轴坐标(根据地图高/食物高):0~19(每个坐标实际像素是20px)显示方法:show食物属性:保存生成的食物(小蛇吃食物、食物刷新时需要用到)蛇Snake: 身体body(默认有三个div组成,其中一个蛇头,两段蛇身,都有颜色)(数组类型)单节宽 width单节高 height单节x坐标单节y坐标移动方向 direction显示方法:show能够移动move方法(能够拐弯,改变移动方向;能够吃食物,变长)2、创建地图div3、创建食物div4、创建蛇div5、让蛇移动6、让蛇拐弯7、让蛇吃食物8、让蛇加速9、边界判断10、蛇不能碰身体11、连续按键无效<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>案例</title> <style type="text/css"> body{margin:0;} </style> </head> <body> <script type="text/javascript"> //创建地图 function Map(){ //属性 this.width = 600; this.height = 400; this.backgroundColor = 'gray'; this.position = 'relative'; this.ditu = null; //方法 if(!Map.prototype.show){ Map.prototype.show = function(){ //显示地图 var div = document.createElement('div'); div.style.width = this.width + 'px'; div.style.height = this.height + 'px'; div.style.backgroundColor = this.backgroundColor; div.style.position = this.position; document.body.appendChild(div); this.ditu = div; } } } var map = new Map(); map.show(); //食物 function Food(map){ //属性 this.width = 20; this.height = 20; this.backgroundColor = 'green'; this.position = 'absolute'; this.x = 0; this.y = 0; this.shiwu = null; //方法 if(!Food.prototype.show){ Food.prototype.show = function(){ //显示随机食物 if(this.shiwu){ var div = this.shiwu; }else{ var div = document.createElement('div'); div.style.width = this.width + 'px'; div.style.height = this.height + 'px'; div.style.backgroundColor = this.backgroundColor; div.style.position = this.position; //将食物显示到地图中 map.ditu.appendChild(div); } //位置 生成随机xy坐标 x 0-40 y 0-30 10 20 while(true){ this.x = Math.floor( Math.random() * (map.width / this.width) ); this.y = Math.floor( Math.random() * (map.height / this.height) ); if(snake){ //判断食物坐标 是否和蛇重合 var flag = true; for(var i=0; i<snake.body.length; i++){ if(this.x == snake.body[i][0] && this.y == snake.body[i][1]){ flag = false; } } if(flag == false){ //重合 continue; }else{ break; } }else{ break; } } div.style.left = this.x * this.width + 'px'; div.style.top = this.y * this.height + 'px'; this.shiwu = div; } } } var food = new Food(map); food.show(); //创建蛇 function Snake(map){ // body [[x坐标,y坐标,背景色,div],[x坐标,y坐标,背景色],[x坐标,y坐标,背景色]] this.body = [[5, 2, 'red', ''],[4, 2, 'blue', ''],[3, 2, 'blue', '']]; //单节身体特点 this.width = 20; this.height = 20; this.position = 'absolute'; this.direction = 'right'; this.canChange = false; //不能改方向 if(!Snake.prototype.show){ Snake.prototype.show = function(){ //给每一节身体 创建div显示 for(var i=0; i<this.body.length; i++){ // this.body[i] 每一节身体信息 if(this.body[i][3]){ var div = this.body[i][3]; }else{ var div = document.createElement('div'); div.style.width = this.width + 'px'; div.style.height = this.height + 'px'; div.style.position = this.position; div.style.backgroundColor = this.body[i][2]; map.ditu.appendChild(div); } //x坐标 this.body[i][0] y坐标 this.body[i][1] 背景色 this.body[i][2] div.style.left = this.body[i][0] * this.width + 'px'; div.style.top = this.body[i][1] * this.height + 'px'; // if(i==0) div.style.borderRadius = '10px'; this.body[i][3] = div; } //每次显示完成,可以改变方向 this.canChange = true; } //让蛇移动 Snake.prototype.move = function(){ //修改坐标 //从蛇尾开始移动(每一节身体 改变坐标) // for(var i=0; i<this.body.length; i++){ // this.body[this.body.length-1-i] // } for(var i=this.body.length-1; i>0; i--){ //将每节身体的坐标,设置为 前一节的坐标 this.body[i][0] = this.body[i-1][0]; this.body[i][1] = this.body[i-1][1]; } //蛇头控制方向 蛇头坐标单独设置 if(this.direction == 'right'){ this.body[0][0] += 1; }else if(this.direction == 'down'){ this.body[0][1] += 1; }else if(this.direction == 'left'){ this.body[0][0] -= 1; }else if(this.direction == 'up'){ this.body[0][1] -= 1; } //边界判断 if(this.body[0][0] < 0 || this.body[0][0] > (map.width/this.width-1) || this.body[0][1] < 0 || this.body[0][1] > (map.height/this.height-1)){ //游戏结束 clearInterval(timer); alert('game over');return; } //不能撞自己 for(var i=1;i<this.body.length;i++){ if(this.body[0][0] == this.body[i][0] && this.body[0][1] == this.body[i][1]){ //游戏结束 clearInterval(timer); alert('game over');return; } } //重新显示 this.show(); //吃到食物,下一次再显示加长的身体 if(this.body[0][0] == food.x && this.body[0][1] == food.y){ //吃到食物 this.body中加一个数组 this.body[this.body.length] = [0, 0, 'blue', '']; // this.body.push([0, 0, 'blue', '']); food.show(); if(time > 150){ time -= 50; setTimer(); } } } } } var snake = new Snake(map); snake.show(); var time = 500; var timer; function setTimer(){ clearInterval(timer); timer = setInterval('snake.move()', time); } setTimer(); //让蛇拐弯 绑定键盘事件 window.onkeyup = function(e){ //根据键盘事件的keyCode var evt = e || window.event; //不能改变方向 if(snake.canChange == false){ return; } if(evt.keyCode == 37 && snake.direction != 'right' ){ snake.direction = 'left'; }else if(evt.keyCode == 38 && snake.direction != 'down'){ snake.direction = 'up'; }else if(evt.keyCode == 39 && snake.direction != 'left'){ snake.direction = 'right'; }else if(evt.keyCode == 40 && snake.direction != 'up'){ snake.direction = 'down'; } snake.canChange = false; } </script> </body> </html>
2022年10月13日
53 阅读
0 评论
1 点赞
2022-10-13
javascript基础-浏览器API
JS基础-浏览器API第0章 API介绍HTML:用来存储网页内容;CSS:用来定义这些内容的显示样式;JavaScript:用来创造丰富的页面效果或者网页应用。0.1 API 介绍API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。任何开发语言都可以有自己的APIAPI的特征输入和输出(I/O)API的使用方法0.2 Web API 接口的概念浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM)此处的Web API特指浏览器给JS提供的API(一组方法),Web API在后面的课程中有其它含义前面我们说过,浏览器的API一共提供了三种类型;分别是 浏览器操控类(BOM)、页面文档操控类(DOM)、网络控制类;但实际上,浏览器提供的API并不只有这三类,而是有很多类:文档对象模型、设备API、通信API、数据管理API、特权API、已认证应用程序的私有API;第1章 文档对象模型 (DOM)1.1 基本概念DOM是JavaScript操作网页的接口,全称为“文档对象模型”(Document Object Model)。 它的作用是将网页转为一个JavaScript对象,从而可以用脚本进行各种操作(增删改查)。浏览器会根据DOM模型,将结构化文档(比如HTML和XML)解析成一系列的节点, 再由这些节点组成一个树状结构(DOM Tree)。 所有的节点和最终的树状结构,都有规范的对外接口。JavaScript是一门编程语言,而DOM是浏览器对HTML文档结构化后的一个模型;严格地说,DOM不属于JavaScript,但是我们最常用的就是使用JavaScript操作DOM;1.2 节点的概念DOM的最小组成单位叫做节点(node)。文档的树形结构(DOM树),就是由各种不同类型的节点组成。每个节点都可以看作是文档树的一片叶子。最顶层的节点就是document节点,它代表了整个文档;是文档的根节点。每张网页都有自己的document节点,window.document属性就指向这个节点的。只要浏览器开始载入HTML文档,这个节点对象就存在了,可以直接调用。每一个HTML标签元素,在DOM树上都会转化成一个Element节点对象;文档里面最高一层一般是HTML标签,其他HTML标签节点都是它的下级。除了根节点以外,其他节点对于周围的节点都存在三种关系:父节点关系(parentNode):直接的那个上级节点 子节点关系(childNodes):直接的下级节点 同级节点关系(sibling):拥有同一个父节点的节点常用dom操作:查找页面的标签元素标签增加、修改、删除等操作标签的属性相关操作给标签元素绑定事件(设置当什么什么时候,做什么什么事情)1.3 查找节点上一节我们知道,整个文档的节点就是document节点,那么想要具体找到某个节点,我们可以使用document提供的一系列方法:<div> <p id="p1">1111111111</p> <i>2222222222</i> <p class='p'>1111111111</p> <i>2222222222</i> <p class="p">1111111111</p> <i>2222222222</i> <div id="p"> <p name="p">3333333333</p> </div> </div>getElementsByTagName()返回所有指定HTML标签的元素,返回值是一个类似数组的HTMLCollection对象;匹配失败,返回[]参数是想要获取节点的具体节点名称,就是 标签名;var p = document.getElementsByTagName('p'); //标签节点.style.样式名 = '样式值' 可以给标签节点设置css样式 p[3].style.background = 'red';getElementsByClassName()返回所有class名字符合指定条件的元素,返回值是一个类似数组的HTMLCollection对象;匹配失败,返回[]参数为 标签的class属性的值var p = document.getElementsByClassName('p'); p[1].style.background = 'yellow';getElementsByName()选择拥有name属性的HTML元素,返回值是一个类似数组的HTMLCollection对象;匹配失败,返回[]参数为 标签的name属性的值;注意,使用时,最好选择原生具有name属性的元素;var p = document.getElementsByName('p'); p[0].style.background = 'yellow';getElementById()返回匹配指定id属性的元素节点;没有发现匹配的节点,则返回null参数为 标签的id属性的值,参数大小写敏感;var p = document.getElementById('p'); p.style.background = 'yellow';querySelector()、querySelectorAll()document.querySelector方法接受一个CSS选择器作为参数,返回匹配该选择器的元素节点;如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null;document.querySelectorAll方法与querySelector用法类似,区别是返回一个类似数组的HTMLCollection对象,包含所有匹配给定选择器的节点。var p = document.querySelector('.p'); p.style.background = 'yellow';var p = document.querySelectorAll('.p'); p[1].style.background = 'yellow';多个参数值,使用,(英文逗号)隔开,而querySelector()返回第一个选中的节点;var p = document.querySelectorAll('i,.p'); for(var i=0;i<p.length;i++){ p[i].style.background = 'yellow'; }这两个方法都支持复杂的CSS选择器。//选中 id 属性值为p1的元素 // var p = document.querySelectorAll('[id="p1"]'); //选中div元素的class属相值为p的元素 // var p = document.querySelectorAll('div.p'); //选中所有的p标签,但是class值为p的除外 var p = document.querySelectorAll('p:not(.p)'); for(var i=0;i<p.length;i++){ p[i].style.background = 'yellow'; }但是,它们不支持CSS伪元素的选择器(比如:first-line和:first-letter)和伪类的选择器(比如:link和:visited),即无法选中伪元素和伪类。第2章 事件2.1 什么是事件一种 触发—响应 的机制;用户的行为 + 浏览器感知(捕获)到用户的行为 + 事件处理程序事件三要素:事件源:(被)触发事件的元素事件类型:事件的触发方式(例如鼠标点击或键盘点击)事件处理程序:事件触发后要执行的代码(函数形式)2.2 事件绑定行内方式绑定(元素属性)<body> <input type="button" value="按钮" id="btn" onclick="alert(2)"> </body> <body> <input type="button" value="按钮" id="btn" onclick="f()"> </body> <script> function f(){ console.log(3); } </script>onclick 其实就是html元素的一个属性,而属性的值需要是 一段可执行的JS代码动态绑定 (节点对象属性)<body> <input type="button" value="按钮" id="btn"> </body> <script> var btn = document.getElementById('btn'); btn.onclick = function(){ alert(4); } </script>获取节点对象,然后 修改 节点对象 的 属性 onclick 的值,值是一个 匿名函数 即可;以上两种事件绑定方式,需要在事件名称前加 on ;事件监听(节点对象方法)<body> <input type="button" value="按钮" id="btn"> </body> <script> var btn = document.getElementById('btn'); btn.addEventListener('click',function(){ alert(5); }); </script>每一个节点对象都提供了 addEventListener 方法,这个方法可以给选中的节点添加指定类型的事件及事件处理程序;移除事件监听<body> <input type="button" value="按钮" id="btn"> </body> <script> function f(){ alert(5); } var btn = document.getElementById('btn'); btn.addEventListener('click',f); btn.removeEventListener('click',f); </script>注意:removeEventListener方法移除的监听函数,必须与对应的addEventListener方法的参数完全一致,而且必须在同一个元素节点,否则无效。2.3 三种事件绑定比较this关键字在JavaScript中,每一个函数的内部都存在一个this关键字,其随着运行环境的不同,其指向也是不同的。<body> <p id="t">ttttt</p> </body> <script> var d = document.getElementById('t'); d.onclick = function(){ //this指代本对象 就是 d console.log(this); } </script>将上述代码的动态绑定,改为行内绑定:<body> <p id="t" onclick="f()">ttttt</p> </body> <script> function f(){ console.log(this); //window对象 } </script>由此可知:行内绑定,其事件处理程序内部的this指向了全局的window对象。动态绑定,其事件处理程序内部的this指向了当前正在操作的dom对象。需求:同一个元素的同一个事件,绑定多个处理函数:<body> <!--行内绑定,谁在前谁执行--> <p id="t" onclick="t()" onclick="f()">ttttt</p> </body> <script> function f(){ alert(1); } function t(){ alert(2); } </script><body> <p id="t" >ttttt</p> </body> <script> //动态绑定,后边的执行函数会将前面的覆盖掉 var d = document.getElementById('t'); d.onclick = function(){ alert(1); } d.onclick = function (){ alert(2); } </script><body> <p id="t" >ttttt</p> </body> <script> var d = document.getElementById('t'); function f1() { console.log(this); } d.addEventListener('click', f1, false); d.addEventListener('click', function(){console.log('f2');}, false); </script>总结:第一种 "HTML标签的on-属性",违反了HTML与JavaScript代码相分离的原则;处理函数中 this 指向的window对象;第二种 "Element节点的事件属性" 的缺点是,同一元素同一个事件只能定义一个监听函数,也就是说,如果定义两次onclick属性,后一次定义会覆盖前一次。但是处理函数中的 this 指向的选中的对象;第三种:addEventListener方法可以针对同一个元素的同一个事件,添加多个监听处理函数。处理函数中的 this 指向的也是选中的元素;2.4 事件类型事件类型一览表:https://developer.mozilla.org/zh-CN/docs/Web/Events2.3.1 页面事件(资源事件)事件名称何时触发load一个资源及其相关资源已完成加载。window.onload=function()<body onload="f1()" > </body> <script> function f1(){ alert('f1'); } </script>2.3.2 焦点事件事件名称何时触发focus元素获得焦点blur元素失去焦点<body> <input type="text" id="t" value="请输入用户名" onfocus="f1()" onblur="f2()"> </body> <script> function f1(){ document.getElementById('t').value = ''; } function f2(){ var v = document.getElementById('t').value; alert(v); } </script>2.3.3 鼠标事件事件名称何时触发mouseenter指针移到有事件监听的元素内mouseover指针移到有事件监听的元素或者它的子元素内mousemove指针在元素内移动时持续触发mousedown在元素上按下任意鼠标按钮mouseup在元素上释放任意鼠标按键click在元素上按下并释放任意鼠标按键dblclick在元素上双击鼠标按钮contextmenu右键点击 (右键菜单显示前).mouseleave指针移出元素范围外(不冒泡)mouseout指针移出元素,或者移到它的子元素上select文本被选中(input标签、textarea标签)copy元素内容被拷贝时 <body> <div id="d" style="width:200px;height:200px;border:1px solid red"> 来啊 </div> </body> <script> var d = document.getElementById('d'); //当鼠标悬浮时触发 d.onmouseover = function(){ console.log('来了?'); } //当鼠标离开时触发 d.onmouseout = function(){ console.log('不要啊'); } //当鼠标按下时触发 d.onmousedown = function(){ console.log('用力啊'); } //当鼠标弹起时触发 d.onmouseup = function(){ console.log('再来'); } //当鼠标移动时触发 d.onmousemove = function(){ console.log('别乱动'); } //当点击右键时 d.oncontextmenu = function(){ console.log('你想干什么?'); return false; } // 当复制内容时 d.oncopy = function(){ console.log('你敢复制我?'); return false; } </script>2.3.4 键盘事件事件名称何时触发keydown按下任意按键keypress除 Shift, Fn, CapsLock 外任意键被按住. (连续触发)keyup释放任意按键<body> <input type="text" value="" id="t"> </body> <script> var d = document.getElementById('t'); //当键盘按下时触发 d.onkeydown = function(){ console.log('推到?'); } //当键盘按下时触发 d.onkeypress = function(){ console.log('撩起2?'); } //当键盘弹起时触发 d.onkeyup = function(){ console.log('撩起?'); } </script>2.3.5 form表单事件Event NameFired Whenreset点击重置按钮时 (<input type=’reset’ value=’重置’ />)submit点击提交按钮<body> <form id="f" action="1.2.5.php"> 姓名:<input type="text" name="" value=""> <br> <input type="submit" name="" value="提交"> <input type="reset" name="" value="重置"> </form> </body> <script> var d = document.getElementById('f'); //当表单提交时触发 d.onsubmit = function(){ alert('t'); } //当表单重置时触发 d.onreset = function(){ alert('re'); } </script>2.3.6 内容变化事件change: 当内容改变且失去焦点时触发 (存储事件)input : 当内容改变时触发 (值变化事件)<body> <input type="text" id="t" value=""> </body> <script> var d = document.getElementById('t'); //当内容改变且失去焦点时触发 d.onchange = function(){ console.log('t'); } //当内容改变时触发 d.oninput = function(){ console.log('in'); } </script>2.4 事件的传播三个包裹着的DIV,都绑定了点击事件,问:当点击 div1 时,会发生什么现象?<head> <title></title> <meta charset="UTF-8"> <style> div{padding: 40px} #div3{width: 300px;height: 300px;background-color: red} #div2{width: 200px;height: 200px;background-color: yellow} #div1{width: 100px;height: 100px;background-color: blue} </style> </head> <body> <div id="div3">3 <div id="div2">2 <div id="div1">1</div> </div> </div> </body> <script> var d1 = document.getElementById('div1'); var d2 = document.getElementById('div2'); var d3 = document.getElementById('div3'); d1.onclick = function(){ alert('1'); } d2.onclick = function(){ alert('2'); } d3.onclick = function(){ alert('3'); } </script>当点击div1时,触发 事件1,但是,紧跟着,事件2和事件3也被触发了;这种现象,我们称为 事件冒泡在JS中当一个事件发生以后,它会在不同的DOM节点之间传播。这种传播分成三个阶段:第一阶段:从window对象传导到目标节点,称为 捕获阶段。第二阶段:在目标节点上触发,称为 目标阶段。第三阶段:从目标节点传导回window对象,称为 冒泡阶段。事件传播的最上层对象是window;事件的传播顺序,在捕获阶段依次为window、document、html、body、div;在冒泡阶段依次为div、body、html、document、window。注意: 三种事件绑定方式全部 默认 监听冒泡阶段事件;2.5 改变事件触发的阶段想让事件监听在捕获阶段,只能通过 addEventListener 方法的进行设置:<script> var d1 = document.getElementById('div1'); var d2 = document.getElementById('div2'); var d3 = document.getElementById('div3'); d1.addEventListener('click',function(){ alert('m1'); });//目标阶段触发 d2.addEventListener('click',function(){ alert('b2'); },true);//捕获阶段触发 d3.addEventListener('click',function(){ alert('b3'); },true);//捕获阶段触发 d1.addEventListener('click',function(){ alert('mm1'); });//目标阶段触发 d2.addEventListener('click',function(){ alert('p2'); });//冒泡阶段触发 d3.addEventListener('click',function(){ alert('p3'); },false);//冒泡阶段触发 </script>2.6 案例为选中的的元素绑定事件<body> <input type="button" value="按钮" id="btn" /> <script> //根据id获取元素 document.getElementById("btn").onclick=function () { alert("哈哈,我又变帅了"); }; </script> </body>一次性事件案例(下载按钮点一次则失效)<body> <input type="button" id="btn" value="下载"> </body> <script> var btn = document.getElementById('btn'); function f(){ alert(123); btn.removeEventListener('click',f); } btn.addEventListener('click',f); </script>点击每个图片弹出对话框<body> <img src="images/1-small.jpg" alt="" /> <img src="images/2-small.jpg" alt="" /> <img src="images/3-small.jpg" alt="" /> <script> //点击每个图片都可以弹出对话框 //根据标签名字获取元素,分别注册点击事件,分别添加事件处理函数 var imgObjs=document.getElementsByTagName("img"); //遍历 for(var i=0;i<imgObjs.length;i++){ //为每个图片元素注册点击事件,添加事件处理函数 imgObjs[i].onclick=function () { alert("啊,我被点击了"); }; } </script> </body>第3章 节点操作页面元素节点的操作,都离不开DOM对象节点:标签(元素)、标签属性、文本内容等3.1 节点操作-增删改document.createElement()用来生成网页元素节点,参数为元素的标签名;document.createTextNode()用来生成文本节点,参数为所要生成的文本节点的内容;node.appendChild()接受一个节点对象作为参数,将其作为最后一个子节点,插入当前节点;node.hasChildNodes()返回一个布尔值,表示当前节点是否有子节点node.removeChild()接受一个子节点作为参数,用于从当前节点移除该子节点node.cloneNode()用于克隆一个选中的节点。它接受一个布尔值作为参数,表示是否同时克隆子节点,默认是false,即不克隆子节点。注意: 不会克隆绑定到该元素上的事件;node.innerHTML返回该元素包含的 HTML 代码。该属性可读写,常用来设置某个节点的内容;(不属于W3C DOM规范)node.innerText返回该元素包含的内容。该属性可读写<body> <div id="d"> <span>111</span> </div> </body> <script> //创建元素节点 var p = document.createElement('p'); //创建文本节点 var t = document.createTextNode('女娲'); //添加节点 p.appendChild(t); var d = document.querySelector('#d') d.appendChild(p); //判断是否有子节点 if(d.hasChildNodes('span')){ var s = document.querySelector('span'); //删除子节点 d.removeChild(s); } // 克隆一个节点 var c = d.cloneNode(true); d.appendChild(c); //操作选中元素的HTML代码,有值则是设置,无值则是获取 alert(d.innerHTML); console.log(document.getElementById('d').innerText); console.log(document.getElementById('d').innerHTML); </script>案例:点击按钮创建img节点,添加到body中<body> <input type="button" value="我要图" id="btn"> </body> <script> var btn = document.getElementById('btn'); btn.onclick = function(){ // var img = document.createElement('img'); // img.src = '/img/c2.jpg'; // document.getElementsByTagName('body')[0].appendChild(img); // 直接 document.getElementsByTagName('body')[0].innerHTML += "<img src='/img/c3.jpg'>"; } </script>动态创建文本框<body> <input type="button" value="++" id="btn"> <div id="bo"></div> </body> <script> var btn = document.getElementById('btn'); btn.onclick = function () { // document.getElementById('bo').innerHTML += '<input type="text">'; var inp = document.createElement('input'); document.getElementById('bo').appendChild(inp); } </script>3.2 节点属性3.2.1 原生属性(固有属性)HTML元素节点的标准属性(即在标准中定义的属性),会自动成为元素节点对象的属性<body> <div id="d" a="b" class="a b c d e"></div> </body> <script> var d = document.querySelector('#d'); //获取原有属性值 console.log(d.id); //修改原有属性值 d.id = 'ff'; console.log(d.a); // undefined //特殊:获取class类名,需使用className属性 console.log(d.className); d.className += ' hello'; </script>3.2.2 属性操作的标准方法node.getAttribute()返回当前元素节点的指定属性。如果指定属性不存在,则返回null;node.setAttribute()为当前元素节点新增属性。如果同名属性已存在<body> <div id="d"></div> </body> <script> var d = document.querySelector('#d'); //设置属性,有则修改,无则添加,可设置非标准属性 d.setAttribute('id','ffdd'); d.setAttribute('aa','kk'); //获取属性值,可获取非标准属性 console.log(d.getAttribute('aa')); </script>node.hasAttribute()返回一个布尔值,表示当前元素节点是否包含指定属性node.removeAttribute()从当前元素节点移除属性//如果有id属性 if(d.hasAttribute('id')){ //删除id属性 d.removeAttribute('id'); }3.3.3 节点的几个属性nodeName nodeName 是只读的 元素节点的 nodeName 与标签名相同 属性节点的 nodeName 与属性名相同 文本节点的 nodeName 始终是 #text 文档节点的 nodeName 始终是 #documentnodeValue 元素节点的 nodeValue 是 undefined 或 null 文本节点的 nodeValue 是文本本身 属性节点的 nodeValue 是属性值nodeType 表示节点的类型 元素 1; 属性 2; 文本 3; 注释 8; 文档 93.3 节点操作-层级关系node.nextElementSibling返回紧跟在当前节点后面的第一个同级Element节点,如果当前节点后面没有同级节点,则返回null;node.previousElementSibling返回紧跟在当前节点前面的第一个同级Element节点,如果当前节点前面没有同级节点,则返回null;node.parentElement返回当前节点的父级Element节点;node.childNodes返回一个NodeList集合,成员包括当前节点的所有子节点(注意空格回车也算)。node.firstChild node.firstElementChild返回树中节点的第一个子节点,如果节点是无子节点,则返回 null。node.lastChild node.firstElementChild返回该节点的最后一个子节点,如果该节点没有子节点则返回null。<body> <div id="d1"> <p id="p1">11111</p> <p id="p2">222</p> <p id="p3">33333</p> <p id="p4">4444</p> </div> <div id="d2"> <p id="p5">55555</p> <p id="p6">66666</p> </div> </body> <script> var p2 = document.querySelector('#p2'); //下一个兄弟节点 p2.nextElementSibling.style.background = 'red'; //上一个兄弟节点 p2.previousElementSibling.style.background = 'red'; //父级节点 p2.parentElement.style.background = 'red'; var d1 = document.querySelector('#d1'); //所有子节点列表 d1.childNodes[3].style.background = 'red'; </script>3.4 CSS样式操作每个DOM对象都有style属性,我们可以直接操作,用来读写 行内CSS样式。之前,我们已经简单的使用过JS控制元素的CSS样式;在具体使用的时候还有一些需要重点注意的细节:名字需要改写,将横杠从CSS属性名中去除,然后将横杠后的第一个字母大写:比如background-color写成backgroundColor属性值都是字符串,设置时必须包括单位:比如,div.style.width的值不能写为100,而要写为100px<body> <div id="d1" style="width:400px;height: 200px;border: 1px solid red"></div> </body> <script> var d1 = document.querySelector('#d1'); d1.onclick = function(){ //赋值则是设置 d1.style.backgroundColor = 'red'; //不赋值则是获取 alert(d1.style.width); } </script>以上代码中,我们获取的CSS样式,均是行内样式;如果将 样式表写在 style 标签内,我们将无法获取和修改;getComputedStyle()接受一个节点对象,返回该节点对象最终样式信息的对象,所谓“最终样式信息”,指的是各种CSS规则叠加后的结果。注意: getComputedStyle() 是window对象下的方法,不是DOM对象<style> #d1{ width: 200px;height: 200px; border: 1px solid red; } </style> <body> <div id="d1" ></div> </body> <script> var d1 = document.querySelector('#d1'); d1.onclick = function(){ //获取不到 console.log(d1.style.width); //获取计算后的样式 console.log(getComputedStyle(d1).width); } </script>点击变大小案例:<head> <meta charset="UTF-8"> <title>Document</title> <style> #d1{ width: 200px;height: 200px; border: 1px solid red; } </style> </head> <body> <div id="d1" ></div> </body> <script> var d1 = document.querySelector('#d1'); d1.onclick = function(){ var w = parseInt(getComputedStyle(d1).width); var h = parseInt(getComputedStyle(d1).height); d1.style.width = w+10+'px'; d1.style.height = h+10+'px'; } </script>其他方法和属性:document.documentURI 返回文档的 URL。node.replaceChild(newChild, oldChild) 用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点。node.insertBefore() : parentElement.insertBefore(newElement,referenceElement);3.5 案例(节点属性)网页开关灯效果实现(类名操作)<style> .cls { background-color: black; } </style> </head> <body> <input type="button" value="开/关灯" id="btn"/> <script> document.getElementById("btn").onclick = function () { //document.body.className="cls"; //console.log(document.body.className); //判断body标签是否应用了cls类样式,同时设置body标签的类样式 document.body.className = document.body.className == "cls" ? "" : "cls"; }; </script>点击按钮显示一个图片<body> <input type="button" value="显示图片" id="btn"/> <img src="" alt="美女" id="im" width="300" height="400" /> <script> //根据id获取按钮,注册点击事件,添加事件处理函数 document.getElementById("btn").onclick=function () { //根据id获取图片标签,设置src属性即可 var imgObj=document.getElementById("im"); //设置路径src属性 imgObj.src="images/liuyan.jpg"; }; </script> </body>案例点击按钮修改p标签内容<body> <input type="button" value="设置p的内容" id="btn"/> <p id="p1">这是一个p</p> <script> //点击按钮,设置p的内容 //根据id获取按钮,注册点击事件,添加事件处理函数 document.getElementById("btn").onclick = function () { //var pObj = document.getElementById("p1"); document.getElementById("p1").innerText = "哦,这是p啊"; }; </script> </body>点击按钮设置a标签的地址和热点文字<input type="button" value="显示效果" id="btn"/> <a href="http://www.baidu.com" id="ak">百度</a> <script> //案例:点击按钮修改a的地址和热点文字 //根据id获取按钮,注册点击事件,添加事件处理函数 document.getElementById("btn").onclick=function () { var aObj=document.getElementById("ak"); aObj.href="http://www.itcast.cn"; aObj.innerText="播客"; }; </script> </body>点击按钮设置所有的p的内容<body> <input type="button" value="改变内容" id="btn"/> <p>红烧榴莲</p> <p>清蒸臭豆腐</p> <p>油炸大蒜</p> <p>爆炒助教</p> <p>凉拌班主任</p> <script> //document.getElementsByTagName("标签的名字"); //点击按钮,修改所有的p的内容 //根据id获取按钮,注册点击事件,添加事件处理函数 document.getElementById("btn").onclick = function () { //获取所有的p标签---根据标签名字来获取---伪数组 var pObjs = document.getElementsByTagName("p"); //循环遍历这个伪数组 for (var i = 0; i < pObjs.length; i++) { pObjs[i].innerText = "我们都是p"; } }; </script> </body>点击按钮修改图片的alt和title<body> <input type="button" value="显示效果" id="btn"/> <img id="im" src="images/cangjingkong.jpg" alt="" title=""/> <script> //点击按钮,修改图片的宽和高,alt和title属性值 //根据id获取按钮,注册点击事件,添加事件处理函数 document.getElementById("btn").onclick = function () { //根据id获取图片标签 var imgObj = document.getElementById("im"); //设置属性 imgObj.width = "500"; imgObj.height = "600"; imgObj.alt = "好漂亮"; imgObj.title = "美女"; }; </script> </body>点击按钮修改按钮的值<body> <input type="button" value="按钮" id="btn"/> <script> //案例:点击按钮修改按钮的value属性值 //根据id获取按钮,注册点击事件,添加事件处理函数 // document.getElementById("btn").onclick=function () { // document.getElementById("btn").value="改变吧"; // }; //在某个元素的自己的事件中,this就是当前的这个元素 document.getElementById("btn").onclick=function () { //当前对象 this.value="改变吧"; }; </script> </body>点击图片修改自身的宽和高<body> <img src="images/boduo.jpg" alt="" id="im" /> <script> //点击图片,修改自身的宽和高 //根据id获取图片,注册点击事件,添加事件处理函数 document.getElementById("im").onclick=function () { this.width="300"; this.height="400"; }; </script> </body>点击按钮修改所有的文本框的值<body> <input type="button" value="显示效果" id="btn"/><br/> <input type="text" value=""/><br/> <input type="text" value=""/><br/> <input type="text" value=""/><br/> <input type="text" value=""/><br/> <input type="text" value=""/><br/> <script> //根据id获取按钮,注册点击事件,添加事件处理函数 document.getElementById("btn").onclick=function () { //根据标签名字获取文本框,所有的input标签 var inputs=document.getElementsByTagName("input"); for(var i=0;i<inputs.length;i++){ //判断当前input是不是文本框 if(inputs[i].type=="text"){ inputs[i].value="我是文本框"; } } }; </script>排他功能<body> <input type="button" value="没怀孕"/> <input type="button" value="没怀孕"/> <input type="button" value="没怀孕"/> <input type="button" value="没怀孕"/> <input type="button" value="没怀孕"/> <script> //获取所有的按钮 var btnObjs = document.getElementsByTagName("input"); //循环,为每个按钮注册点击事件,添加事件处理函数 for (var i = 0; i < btnObjs.length; i++) { //每个按钮注册点击事件 btnObjs[i].onclick = function () { //把所有的按钮的value值还原 for (var j = 0; j < btnObjs.length; j++) { btnObjs[j].value = "没怀孕"; } //设置当前的这个按钮的value this.value = "怀孕了"; }; } </script>点击按钮禁用文本框<input type="button" value="禁用文本框" id="btn"/> <input type="text" value="" id="txt"/> <script src="common.js"></script> <script> //点击按钮禁用这个文本框 document.getElementById("btn").onclick = function () { document.getElementById("txt").disabled = true; }; </script>鹦鹉学舌<body> <input type="text" id="t1" > <br> <input type="text" id="t2" > </body> <script> document.getElementById('t1').oninput = function(){ document.getElementById('t2').value = this.value; } </script>全选和全不选<style> table { border-collapse: collapse; border-spacing: 0; border: 1px solid #c0c0c0; width: 500px; } th, td { border: 1px solid #d0d0d0; color: #404060; padding: 10px; } </style> <body> <table> <thead> <tr> <th> <input type="checkbox" id="th" /> </th> <th>菜名</th> <th>饭店</th> </tr> </thead> <tbody id="tb"> <tr> <td> <input type="checkbox" /> </td> <td>红烧肉</td> <td>田老师</td> </tr> <tr> <td> <input type="checkbox" /> </td> <td>西红柿鸡蛋</td> <td>田老师</td> </tr> <tr> <td> <input type="checkbox" /> </td> <td>油炸榴莲</td> <td>田老师</td> </tr> <tr> <td> <input type="checkbox" /> </td> <td>清蒸助教</td> <td>田老师</td> </tr> </tbody> </table> </body> <script> document.getElementById('th').onclick = function () { var tb = document.getElementsByTagName('input'); if (this.checked == true) { // console.log(tb); for (var i = 0; i < tb.length; i++) { tb[i].checked = true; } } else { for (var i = 0; i < tb.length; i++) { tb[i].checked = false; } } } </script>练习,实现反选表格鼠标悬浮高亮(接上一个案例)var t = document.getElementsByTagName('tbody')[0]; t.onmouseover = function(e){ e.target.parentElement.style.background = '#F5F5F5'; } t.onmouseout = function(e){ e.target.parentElement.style.background = '#fff'; }3.6 案例(样式操作)节点方式隔行变色(了解即可)<body> <input type="button" value="隔行变色" id="btn" /> <ul id="uu"> <li>雪花啤酒</li> <li>金士百啤酒</li> <li>青岛啤酒</li> <li>燕京啤酒</li> <li>百威啤酒</li> <li>哈尔滨啤酒</li> <li>乐宝啤酒</li> <li>崂山啤酒</li> </ul> </body> <script> //点击按钮,所有的li隔行变色---奇红偶黄 document.getElementById("btn").onclick = function () { var count = 0; //要获取ul中所有的子节点 var nodes = document.getElementById("uu").childNodes; for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; //判断这个节点是不是li if (node.nodeType == "1" && node.nodeName == "LI") { node.style.backgroundColor = count % 2 == 0 ? "red" : "yellow"; count++;//记录li标签的个数 } } }; </script>验证密码的长度改变背景颜色<input type="text" value="" id="txt"/> <script> //根据id获取文本框---失去焦点的事件 document.getElementById("txt").onblur=function () { //判断文本框中输入的内容长度是否在6到10个之间,如果是这样的,则背景颜色为绿色 if(this.value.length>=6&&this.value.length<=10){ this.style.backgroundColor="green"; }else{ this.style.backgroundColor="red"; } }; </script>div的高亮显示<style> div { width: 200px; height: 200px; background-color: green; float: left; margin-right: 20px; cursor: pointer; border: 2px solid green; } </style> <body> <div></div> <div></div> <div></div> <div></div> <div></div> <script> //根据标签名字获取所有的div var divObjs = document.getElementsByTagName("div"); //循环遍历 for (var i = 0; i < divObjs.length; i++) { //为每个div添加鼠标进入事件 divObjs[i].onmouseover = mouseoverHandle; //为每个div添加鼠标离开事件 divObjs[i].onmouseout = mouseoutHandle; } function mouseoverHandle() { this.style.border = "2px solid red"; } function mouseoutHandle() { //希望这个样式属性的值还原成默认的时候,值就是""空的字符串 this.style.border = ""; } </script>点击按钮设置div的宽和高及背景颜色<head> <meta charset="UTF-8"> <title>title</title> <style> div{ width: 100px; height: 50px; background-color: yellow; } </style> </head> <body> <input type="button" value="显示效果" id="btn"/> <div id="dv"></div> <script> //点击按钮,设置div的宽和高,及背景颜色 //根据id获取按钮,注册点击事件,添加事件处理函数 document.getElementById("btn").onclick=function () { //获取div var dvObj=document.getElementById("dv"); dvObj.style.width="300px"; dvObj.style.height="200px"; //css中的属性如果是多个单词连接的,在js代码DOM操作中多个单词中间的-干掉,后面单词的首字母变大写 dvObj.style.backgroundColor="pink"; }; </script>点击按钮隐藏div <style> div{ width: 200px; height: 100px; background-color: orangered; } </style> </head> <body> <input type="button" value="隐藏" id="btn"/> <input type="button" value="显示" id="btn2"/> <div id="dv"></div> <script src="common.js"></script> <script> //点击按钮隐藏div document.getElementById("btn").onclick=function () { //获取div,隐藏 document.getElementById("dv").style.display="none"; }; document.getElementById("btn2").onclick=function () { document.getElementById("dv").style.display="block"; }; </script>点击按钮改变列表的背景颜色<input type="button" value="改变颜色" id="btn"/> <ul id="uu"> <li>乔峰</li> <li>卡卡西</li> <li>佐助</li> <li>自来也</li> <li>纲手</li> <li>雏田</li> <li>露琪亚</li> </ul> <script src="common.js"></script> <script> //获取按钮,注册点击事件,添加事件处理函数 document.getElementById("btn").onclick=function () { document.getElementById("uu").style.backgroundColor="pink"; }; </script>点击按钮列表隔行变色<input type="button" value="隔行变色" id="btn"/> <ul id="uu"> <li>五菱宏光</li> <li>路虎</li> <li>兰博基尼</li> <li>布加迪威龙</li> <li>玛莎拉蒂</li> <li>奥拓</li> <li>拖拉机</li> <li>桑塔纳</li> </ul> <script> //点击按钮,li隔行变色:奇红偶黄 //获取按钮,添加点击事件 document.getElementById("btn").onclick = function () { //获取id为uu的ul中所有的li var list = getElementsByTagName("li"); //遍历 for (var i = 0; i < list.length; i++) { // if (i % 2 == 0) { // list[i].style.backgroundColor = "red"; // } else { // list[i].style.backgroundColor = "yellow"; // } list[i].style.backgroundColor = i % 2 == 0 ? "red" : "yellow"; } }; </script>3.7 案例(节点操作)动态可编辑表格<style> table { border-collapse: collapse; border-spacing: 0; border: 1px solid #c0c0c0; width: 500px; } th, td { border: 1px solid #d0d0d0; color: #404060; padding: 10px; } </style> <body> <input type="button" value="添加一行" id="addrow"> <table> <thead> <tr> <td>菜名</td> <td>饭店</td> <td>厨师</td> </tr> </thead> <tbody id="tb"></tbody> </table> </body> <script> var tbs = document.getElementsByTagName('table')[0]; tbs.onclick = function (e) { var clicks = e.target; if (clicks.nodeName == 'TD') { // var inp = document.createElement('input'); // inp.value = clicks.innerText; // console.log(clicks.parentElement); clicks.innerHTML = '<input value=' + clicks.innerText + '>'; tbs.getElementsByTagName('input')[0].onblur = function () { clicks.innerHTML = this.value; } } } var addrow = document.getElementById('addrow'); addrow.onclick = function(){ var tr = document.createElement('tr'); for(var i = 0; i<3;i++){ tr.appendChild(document.createElement('td')); } document.getElementById('tb').appendChild(tr); } </script>无刷新评论<body> <div id="cont"> <div> <p>name:</p> <p>我是评论内容</p> <hr> </div> </div> <div> 昵称: <input type="text" value="" id="userName" /><br/><br/> <textarea name="" id="tt" cols="103" rows="10"></textarea><br/> <input type="button" value="评论一下" id="btn" /><br/> </div> </body> <script> var un = document.getElementById('userName'); var tt = document.getElementById('tt'); document.getElementById('btn').onclick = function(){ var d = document.createElement('div'); d.innerHTML = '<p>'+un.value+':</p>'; d.innerHTML += '<p>'+tt.value+'</p>'; d.innerHTML += '<hr>'; document.getElementById('cont').appendChild(d); un.value = ''; tt.value = ''; } </script>第4章 事件对象4.1 概述事件的触发,大部分情况下是用户的一种行为,也就是说,我们并不能确定用户什么时间触发;而且,由于事件的传播机制,我们甚至不能确定事件具体触发在哪个节点;这是一件很不爽的事情;如何解决呢?事件发生以后,系统会调用我们写好的事件处理程序系统会在调用处理程序时,将事件发生时有关事件的一切信息,封装成一个对象,作为参数传给监听函数(事件处理程序),我们把这个对象称为 事件对象。有关事件发生的一切信息,都包含在这个事件对象中;根据事件类型的不同,事件对象中包含的信息也有所不同;如点击事件中,包含鼠标点击的横纵坐标位置,键盘事件中,包含键盘的键值等;事件对象获取方式:标准浏览器:事件函数第一个参数;IE浏览器 :window.event<body> <div id="div"> <p>pppp</p> </div> <input type="text" value="" id="i"> </body> <script> var d = document.getElementById('div'); //鼠标事件 d.addEventListener('click',function(e){ console.log(e); }); var i = document.getElementById('i'); //键盘事件 i.addEventListener('keydown',k); function k(e){ console.log(e); } </script>4.2 事件对象中的常用属性及方法4.2.1属性event.bubbles:属性返回一个布尔值,表示当前事件是否会冒泡;event.eventPhase:返回一个整数值,表示事件流在传播阶段的位置0:事件目前没有发生。1:事件目前处于捕获阶段。2:事件到达目标节点。3:事件处于冒泡阶段。event.type:返回一个字符串,表示事件类型,大小写敏感;event.timeStamp:返回一个毫秒时间戳,表示事件发生的时间;clientX、clientY :获取鼠标事件触发的坐标(可视化界面--不包含工具栏等位置) (通用)pageX pageY(标准浏览器) offsetX offsetY(IE浏览器)key、keyCode:获取键盘事件按键输入的字符和按键编号<body> <div id="d"> <p id="p">sdf</p> </div> </body> <script> var p = document.getElementById('p'); p.onclick = function(e){ //当前事件是否会冒泡 console.log(e.bubbles); //事件目前所处的节点 console.log(e.eventPhase); //事件类型 console.log(e.type); //事件发生的时间戳 console.log(e.timeStamp); } </script>4.2.2 事件代理/委托event.target:对事件起源目标的引用,属性返回触发事件的那个节点。event.currentTarget:属性返回事件当前所在的节点,即正在执行的监听函数所绑定的那个节点。作为比较,target属性返回事件发生的节点。var d = document.getElementById('d'); d.onclick = function(e){ //返回事件节点 console.log(e.currentTarget); //返回触发节点 console.log(e.target); }由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理也叫 事件委托 也有人称为 事件代理<head> <title></title> <meta charset="UTF-8"> <style> div{padding: 40px} #div3{width: 300px;height: 300px;border: 1px solid red;} #div2{width: 200px;height: 200px;border: 1px solid red;} #div1{width: 100px;height: 100px;border: 1px solid red} </style> </head> <body> <div id="div3">3 <div id="div2">2 <div id="div1">1</div> </div> </div> </body> <script> var d = document.getElementById('div3'); d.onclick = function(e){ e.target.style.background = 'red'; } </script>4.3.3 阻止浏览器默认行为&阻止事件传播标准浏览器(W3C内核,非IE)event.preventDefault() ;IE浏览器 event.returnValue = false;通用:return false;方法取消浏览器对当前事件的默认行为,比如点击链接后,浏览器跳转到指定页面,或者按一下空格键,页面向下滚动一段距离。if(event.preventDefault){ event.prevetDefault();}else{ event.returnValue = false;}标准浏览器(W3C内核,非IE)event.stopPropagation() ;IE浏览器 event.cancelBubble= true;方法阻止事件在DOM中继续传播,防止再触发定义在别的节点上的监听函数if(event.stopPropagation){ event.stopPropagation();}else{ event.cancelBubble= true;}<body> <div id="div2">2 <div id="div1">1 <a id="a" href="http://qq.com">王者喝农药</a> </div> </div> </body> <script> var d2 = document.getElementById('div2'); var d1 = document.getElementById('div1'); var a = document.getElementById('a'); d2.onclick = function(e){ alert('d2'); } d1.onclick = function(e){ alert('d1'); } a.onclick = function(e){ //阻止事件传播 // e.stopPropagation(); alert('a'); //阻止浏览器默认行为 e.preventDefault(); } </script>4.3 案例阻止超链接默认跳转<body> <!--第1种写法--> <a href="http://www.baidu.com" onclick="alert('哈哈'); return false;">百度</a> <!--第2种写法--> <script> function f1() { alert("嘎嘎"); return false; } </script> <a href="http://www.baidu.com" onclick="return f1();">百度</a> <!--第3种写法--> <a href="http://www.baidu.com" id="ak">百度</a> <script> document.getElementById("ak").onclick=function () { alert("哈哈"); return false; }; </script>点击小图显示大图<body> <a id="ak" href="images/1.jpg"><img src="images/1-small.jpg" alt="" id="im"></a> <script> //点击小图片,显示大图---修改了这个图片标签的src的属性值 //根据id获取小图,注册点击事件,添加事件处理函数 document.getElementById("im").onclick=function () { //根据id获取超链接 var aObj=document.getElementById("ak"); this.src=aObj.href; //阻止超链接的默认跳转事件 return false; }; </script>鼠标点哪图片飞到哪里思路:给整个dom绑定鼠标点击事件,获取点击位置后修改图片位置<style> img { position: absolute; width: 50px; height: 50px; } </style> <body> <img src="/img/c3.jpg" alt=""> </body> <script> var im = document.getElementsByTagName('img')[0]; document.onclick = function (e) { im.style.left = e.clientX + 'px'; im.style.top = e.clientY + 'px'; } </script>跟着鼠标飞的天使思路:点击图片后,给整个dom绑定鼠标移动事件,让图片跟随<style> img { position: absolute; width: 50px; height: 50px; } </style> <body> <img src="/img/c3.jpg" alt=""> </body> <script> var im = document.getElementsByTagName('img')[0]; im.onclick = function () { document.onmousemove = function (e) { im.style.left = e.clientX + 'px'; im.style.top = e.clientY + 'px'; } } </script>实时获取鼠标在div内的坐标<body> <div id="div3">3 <p id="p"></p> </div> </body> <script> var p = document.getElementById('p'); document.getElementById('div3').onmousemove = function(e){ p.innerHTML = e.clientX+','+e.clientY; p.style.top = e.clientY+"px"; p.style.left = e.clientX+"px"; } </script>第5章 浏览器对象模型5.1 介绍浏览器对象模型(Browser Object Model)--英文简称 BOM,浏览器对象模型提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。我们使用JavaScript与浏览器交互的所有内容,均来自 浏览器对象模型。浏览器对象模型的具体实例化对象就是 window 对象;window 对象下有很多属性和方法,我们前面学过的DOM对象,就是window对象的一个属性,只不过这个属性的值又是一个对象,因此也成为window对象的子对象;https://developer.mozilla.org/zh-CN/docs/Web/API/Window5.2 对话框window.alert() : 显示一个警告对话框,上面显示有指定的文本内容以及一个"确定"按钮。window.prompt() : 显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字.var s = window.prompt('你觉得很幸运吗?','是的'); console.log(s);window.confirm() :方法显示一个具有一个可选消息和两个按钮(确定和取消)的模态对话框 。5.3 页面加载事件onloadwindow.onload = function () { // 当页面加载完成执行 // 当页面完全加载所有内容(包括图像、脚本文件、CSS 文件等)执行 }5.4 浏览器控制台window.console : 返回console对象的引用,该对象提供了对浏览器调试控制台的访问。Console.clear() : 清空控制台。Console.error() : 打印一条错误信息Console.table() : 将数组或对象数据在控制台以表格形式打印Console.log() : 打印字符串,使用方法比较类似C的printf、PHP的echo等格式输出5.5 定时器setTimeout()和clearTimeout()在指定的毫秒数到达之后执行指定的函数,只执行一次// 创建一个定时器,1000毫秒后执行,返回定时器的标示 var timerId = window.setTimeout(function () { console.log('Hello World'); }, 1000); // 取消定时器的执行 window.clearTimeout(timerId);setInterval()和clearInterval()定时调用的函数,可以按照给定的时间(单位毫秒)周期调用函数// 创建一个定时器,每隔1秒调用一次 var timerId = window.setInterval(function () { var date = new Date(); console.log(date.toLocaleTimeString()); }, 1000); window.clearInterval(timerId);5.6 案例一起来摇摆<style> #div{ position: absolute; } </style> <body> <input type="button" value="摇起来" id="btn1"> <div id="div"> <img src="/img/c2.jpg" style="width: 50px;height: 50px;" alt=""> <img src="/img/c3.jpg" style="width: 50px;height: 50px;" alt=""> </div> </body> <script> var b1 = document.getElementById('btn1'); var b2 = document.getElementById('btn2'); b1.onclick = function(){ if(this.value != '停止'){ this.value = '停止'; var d = document.getElementById('div'); c = window.setInterval(function(){ d.style.left = parseInt(Math.random() * 100 +1) + 'px' d.style.top = parseInt(Math.random() * 100 +1) + 'px' },50); }else{ this.value = '摇起来'; clearInterval(c); } } </script> 眼看着图片就这么飞走了<style> #div{ position: absolute; } </style> <body> <input type="button" value="摇起来" id="btn1"> <div id="div"> <img src="/img/c2.jpg" style="width: 50px;height: 50px;" alt=""> </div> </body> <script> var b1 = document.getElementById('btn1'); b1.onclick = function(){ var d = document.getElementById('div'); window.setInterval(function(){ var s = parseInt(getComputedStyle(d)['top']); var l = parseInt(getComputedStyle(d)['left']); // console.log(l); d.style.top = s-1+'px'; d.style.left = l+5+'px'; },50) } </script> 5.7 location对象https://developer.mozilla.org/zh-CN/docs/Web/API/Locationwindow.location 只读属性,返回一个 Location对象,其中包含有关文档当前位置的信息;URL统一资源定位符 (Uniform Resource Locator, URL)URL的组成:scheme://host:port/path?query#fragment scheme:通信协议 常用的http,ftp,maito等 host:主机 服务器(计算机)域名系统 (DNS) 主机名或 IP 地址。 port:端口号 整数,可选,省略时使用方案的默认端口,如http的默认端口为80。 path:路径 由零或多个'/'符号隔开的字符串,一般用来表示主机上的一个目录或文件地址。 query:查询 可选,用于给动态网页传递参数,可有多个参数,用'&'符号隔开,每个参数的名和值用'='符号隔开。例如:name=zs fragment:信息片断 字符串,锚点.console.log(location);页面跳转location.href = 'http://qq.com'5.8 history对象https://developer.mozilla.org/zh-CN/docs/Web/API/Historyhistory.back() : 前往上一页, 用户可点击浏览器左上角的返回按钮模拟此方法history.forward() : 在浏览器历史记录里前往下一页,用户可点击浏览器左上角的前进按钮模拟此方法history.go() : 通过当前页面的相对位置从浏览器历史记录( 会话记录 )加载页面。比如:参数为-1的时候为上一页,参数为1的时候为下一页.5.9 navigator对象https://developer.mozilla.org/zh-CN/docs/Web/API/Navigatornavigator.userAgent : 通过userAgent可以判断用户浏览器的类型navigator.platform : 通过platform可以判断浏览器所在的系统平台类型.navigator.geolocation : 位置定位对象;参考: https://developer.mozilla.org/zh-CN/docs/Web/Reference/API
2022年10月13日
22 阅读
0 评论
0 点赞
2022-10-13
JavaScript基础
第1章 JavaScript介绍1.1 JavaScript编程语言JavaScript,简称JS,是一种客户端脚本语言,主要用来向HTML网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。可以直接嵌入HTML页面,但写成单独的js文件有利于结构和行为的分离。在绝大多数浏览器的支持下,可以在多种平台下运行(如Windows、Linux、Mac、Android、iOS等)JS主要运行于客户端(浏览器),也可以运行在服务端(操作系统)JavaScript 和 Java 是两种不同的编程语言:JavaScript主要用于客户端,Java用于服务端。JavaScript现在的意义(应用场景)JavaScript 发展到现在几乎无所不能。网页特效 *服务端开发(Node.js) *命令行工具(Node.js)桌面程序(Electron)App(Cordova)控制硬件-物联网(Ruff)游戏开发(cocos2d-js)1.2 发展及历史1994年Netscape公司推出免费版本浏览器 Netscape Navigator(网景浏览器1.0)1995年微软公司发布 Internet Explorer 1.0。1995年网景公司为适应市场变化,需要开发一门专门在浏览器运行的脚本语言,这个任务交给了布兰登,为了应付公司安排的任务,他只用10天时间就提交了工作,并将这门语言命名为 LiveScript;后来为了蹭sun公司java的热度,与sun公司合作,将其临时改名为“JavaScript”;1996年8月,微软模仿JavaScript开发了一种相近的语言,取名为JScript,首先内置于IE 3.01997年7月,ECMA组织发布ECMAScript 1.0版;此后,明争暗斗不断,1998年6月,ECMAScript 2.0版发布,1999年12月,ECMAScript 3.0版发布;2007年10月,ECMAScript 4.0版草案发布,2008年7月中止ECMAScript 4.0的开发,并发布3.1版本;会后不久,ECMAScript 3.1就改名为ECMAScript 5。2011年6月,ECMAscript 5.1版发布,现在使用最为广泛的版本 版发布,现在使用最为广泛的版本;2015年6月,ECMAScript 6正式发布,并且更名为“ECMAScript 2015”;随后,ECMA组织决定,每年发布一个升级版本,以年号来代替版本号,如:ECMAScript 2016、ECMAScript 2017;另外:1996年,样式表标准CSS第一版发布;1997年,DOM模式第一版正式应用,目前的通用版本是DOM3,下一代版本DOM 4正在拟定中。1999年,IE5部署了XMLHttpRequest接口,允许JavaScript发出HTTP请求;2001年,提出了JSON格式,用于取代XML格式。2002年,Mozilla项目发布第一版Firefox。2003年,苹果公司发布了Safari浏览器的第一版。2006年,jQuery函数库诞生2007年,Webkit引擎在iPhone手机中得到部署;2008年,为Chrome浏览器而开发的V8编译器(解析引擎)诞生;2009年,基于V8解析引擎的Node.js项目诞生,迎来前后端JS的霸权时代;2009年,Google发布Chrome OS2009年,Google发布Angular框架;2013年,Mozilla基金会发布手机操作系统Firefox OS,该操作系统的整个用户界面都使用JavaScript;2013年5月,Facebook发布UI框架库React;2014年,尤雨溪发布开源前端开发库Vue.js;2015年3月,Facebook公司发布了 React Native项目;1.3 JavaScript和HTML、CSSHTML:提供网页的结构,提供网页中的内容CSS: 用来样式排版、美化网页JavaScript: 可以用来控制网页内容,给网页增加动态的效果1.4 JavaScript的组成ECMA 欧洲计算机制造联合会;ECMAScript 是一套标准,定义了一种语言的标准,规定了基本语法、数据类型、关键字、具体API的设计规范等,解析引擎设计的参考标准,但与具体实现无关;1.4.1 ECMAScript - JavaScript的核心ECMAScript是一套语法标准,描述了JavaScript语言的基本语法和数据类型,是JavaScript的核心。 ES5 ES61.4.2 BOM - 浏览器对象模型一套操作浏览器功能的API通过BOM可以操作浏览器窗口,比如:弹出框、控制浏览器跳转、获取分辨率等1.4.3 DOM - 文档对象模型一套操作页面元素的APIDOM可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作1.5 JS学习概况我们在学习JS时,需要学习的内容分为两部分,语言结构及宿主环境提供的API;语言结构部分主要时语言规则及内置对象;而宿主环境的API,根据宿主环境不同而不同,以浏览器为例(js还可以运行在服务器/操作系统),最常见的三个类型:浏览器控制类、DOM操作类、网络控制类;总结:JavaScript编程语言简称 JS,是一种嵌入式的脚本语言,应用范围及其广泛,由布兰登-艾奇开发,在20+年的发展中历经沧桑,学习 JS 分为语言规则及宿主环境两部分;第2章 入门2.1 如何写一段JS代码并运行写在行内01.html: <input type="button" value="按钮" onclick="alert('Hello World')" />写在script标签中 *02.html: <head> <script> alert('Hello World!'); </script> </head>写在外部js文件中,在页面引入03.html: <script src="main.js"></script>main.js: alert('Hello World!');注意点: 引用外部js文件的 script 标签中不可以再写JavaScript代码,即使写了也不会执行,没有作用温馨提示:下面开始进入 JS 基础语法的学习,非常枯燥,别睡着……音乐很优美很动听,但学五线谱真的是乏味无聊痛苦不堪;2.3 变量2.3.1 什么是变量什么是变量变量是计算机内存中存储数据的标识符,根据变量名称可以获取到内存中存储的数据为什么要使用变量使用变量可以方便的获取或者修改内存中的数据变量就是存储数据的容器;2.3.2 如何使用变量var声明变量var age;变量的赋值var age; age = 18;同时声明多个变量var age, name, sex; age = 10; name = 'zs';同时声明多个变量并赋值var age = 10, name = 'zs';2.3.3 变量的命名规则和规范规则 - 必须遵守的,不遵守会报错由字母、数字、下划线、$符号组成,且不能以数字开头区分大小写不能是关键字和保留字,例如:for、while。规范 - 建议遵守的,不遵守不会报错变量名必须有意义遵守驼峰命名法。(首字母小写,后面单词的首字母需要大写。例如:userName、userPassword)下面哪些变量名不合法a 1 age18 18age name $ $name _sex &sex theworld theWorldname变量名,本身不是保留字/关键字, 建议少用。 name在有的浏览器中,是自动声明过的。2.3.4 案例交换两个变量的值var a = '1'; var b = '2'; // 借助第三个变量 var c = a; a=b; b=c; console.log(a,b);不使用临时变量,交换两个数值变量的值//第二种方式 var num1 = 10; var num2 = 20; //计算的方式:累加,然后相减 num1 = num1 + num2;//num1的结果是30 num2 = num1 - num2;//num2的结果是10 num1 = num1 - num2;//num1的结果是20 console.log(num1); console.log(num2);2.3.5 代码调试(输出变量)alert 弹框 :浏览器页面弹框var num1 = 10; alert(num1);console.log() 浏览器console控制台var num1 = 10; var num2 = 20; console.log(num1); console.log(num1, num2);document.write() 浏览器页面中var num1 = 10; document.write(num1);2.4 数据类型2.4.1 简单数据类型Number、String、Boolean、Undefined、Null获取变量的类型typeofvar age = 18; console.log(typeof age); // 'number'Number类型数值字面量:数值的固定值的表示法(数值直接量)110 1024 60.5浮点数(小数)浮点数的精度问题浮点数 var n = 5e-324; // 科学计数法 5乘以10的-324次方 浮点数值的最高精度是 17 位小数,但在进行算术计算时其精确度远远不如整数 var result = 0.1 + 0.2; // 结果不是 0.3,而是:0.30000000000000004 console.log(0.07 * 100); 不要判断两个浮点数是否相等数值范围最小值:Number.MIN_VALUE,// 这个值为: 5e-324 5乘以10的-324次方 最大值:Number.MAX_VALUE,// 这个值为: 1.7976931348623157e+308 无穷大:Infinity 无穷小:-InfinityString类型'abc' "abc" 单双引号引起来的一连串字符字符串字面量(直接量)'程序猿','程序媛', "黑马程序猿"思考:如何打印以下字符串。我是一个 "正直" 的人我很喜欢 "黑马 '程序猿' "注: 转义后单双引号 只能 就近和转义后的单双引号进行配对转义符注: \b 退格符 \f又叫走纸或进纸或 换页符var s = '我很喜欢 \"黑马 \'程序猿\' \"'; console.log(s);字符串长度length属性用来获取字符串的长度var str = '黑马程序猿 Hello World'; console.log(str.length);字符串拼接字符串拼接使用 + 连接console.log(11 + 11); console.log('hello' + ' world'); console.log('100' + '100'); console.log('11' + 11); console.log('male:' + true);两边只要有一个是字符串,那么+就是字符串拼接功能两边如果都是数字,那么就是算术功能。Boolean类型Boolean字面量: true和false,区分大小写计算机内部存储:true为1,false为0Undefined和Nullundefined表示一个声明了没有赋值的变量,变量只声明的时候值默认是undefinednull表示一个空,变量的值如果想为null,必须手动设置注: 关于undefined和null是一个面试中很容易被问到的问题2.4.2 复杂数据类型 Object 对象:保存很多数据的一种数据类型后面详解;题外话如何使用谷歌浏览器,快速的查看数据类型?字符串的颜色是黑色的,数值类型是蓝色的,布尔类型也是蓝色的,undefined和null是灰色的console.log('ss',2,null,undefined,true);2.5 注释被注释的内容是不执行的,不管什么内容都不会运行;单行注释用来描述下面一个或多行代码的作用// 这是一个变量 var name = 'hm';多行注释用来注释多条代码/* var age = 18; var name = 'zs'; console.log(name, age); */总结:JS代码的书写,变量的声明方式,数据类型,注释;第3章 数据类型转换3.1 转换成字符串类型toString()var num = 5; console.log(num.toString());String()var s = null; console.log(s.toString()); console.log(String(s)); // String()函数存在的意义:有些值没有toString(), // 这个时候可以使用String()。比如:undefined和null拼接字符串方式num + "",当 + 两边一个操作符是字符串类型,一个操作符是其它类型的时候,会先把其它类型转换成字符串再进行字符串拼接,返回字符串3.2 转换成数值类型Number()var a = Number('1'); var b = Number(1); var c = Number('c'); var d = Number(null); var e = Number(undefined); console.log(a,b,c,d,e); // 1 1 NaN 0 NaN // Number()可以把任意值转换成数值,如果要转换的字符串中有一个不是数值的字符,返回NaNparseInt()var a = parseInt('1.2df'); var b = parseInt(1); var c = parseInt('c12'); var d = parseInt(null); var e = parseInt(undefined); console.log(a,b,c,d,e); //1 1 NaN NaN NaN // 如果第一个字符是数字会解析,直到遇到非数字结束 // 如果第一个字符不是数字或者符号就返回NaNparseFloat()var a = parseFloat('1.2df'); var b = parseFloat('1.3.4'); var c = parseFloat('c12'); var d = parseFloat(null); var e = parseFloat(undefined); console.log(a,b,c,d,e); //1.2 1.3 NaN NaN NaN // parseFloat() 把字符串转换成浮点数 // parseFloat()和parseInt非常相似, // 不同之处在与parseFloat会解析第一个 . 遇到第二个.或者非数字结束 // 如果解析的内容里只有整数,解析成整数+,-,-0 等运算var str = '500'; console.log(+str); // 取正 console.log(-str); // 取负 console.log(str - 0); 3.3 转换成布尔类型Boolean()var a = Boolean('0'); var b = Boolean(0); var c = Boolean('1'); var d = Boolean(null); var e = Boolean(undefined); var f = Boolean(NaN); console.log(a,b,c,d,e,f); //true false true false false false // 0、''(空字符串) 、null、 undefined 、NaN 会转换成false 其它都会转换成true总结:字符串、数值及布尔类型的数据类型转换第4章 操作符表达式:值和操作符,运算会有一个结果;同时,表达式中的每个数值及部分表达式,又称为 子表达式4.1 算术运算符+ - * / % 取余(取模)4.2 一元运算符 *一元运算符:只有一个操作数的运算符,一元运算会直接修改原始变量的数据;5 + 6 两个操作数的运算符 二元运算符++ 自身加 (自增)-- 自身减 (自减)前置++var num1 = 5; ++ num1; var num2 = 6; console.log(num1 + ++ num2); //13后置++var num1 = 5; num1 ++; var num2 = 6 console.log(num1 + num2 ++); //12猜猜看var a = 1; var b = ++a + ++a; console.log(b); //5 var a = 1; var b = a++ + ++a; console.log(b);//4 var a = 1; var b = a++ + a++; // console.log(b); // 3 var a = 1; var b = ++a + a++; console.log(b);//4总结前置++:先加1,后参与运算后置++:先参与运算,后加1后置++ 运算的两个条件,满其一就会执行1:整个表达式结束;2表达式没结束但是又被使用了;上面两个理解后,下面两个自通前置-- :先减1,后参与运算后置-- :先参与运算,后减14.3 逻辑运算符(布尔运算符) *&& 与 左边为真则取右边,左边为假则取左边 || 或 左边为真则取左边,左边为假则边右边 ! 非 取反var a = 1; var b = 2; var c = 0; console.log(a || b); //1 console.log(b || a); //2 console.log(c && a); //0 console.log(a || c && b); //1 // JS逻辑运算中的逻辑或和逻辑与的运算结果: // 决定整个表达式的子表达式的值4.4 关系运算符(比较运算符)< > >= <= == != === !====与===的区别:==只进行值得比较,===类型和值同时相等,则相等 var result = '55' == 55; // true var result = '55' === 55; // false 值相等,类型不相等 var result = 55 === 55; // true4.5 赋值运算符注意与数学符号的差别;= += -= *= /= %=例如: var num = 0; num += 5; //相当于 num = num + 5;4.6 运算符的优先级 *优先级从高到底 1. () 优先级最高 2. 一元运算符 ++ -- ! 3. 算数运算符 先* / % 后 + - 4. 关系运算符 > >= < <= 5. 相等运算符 == != === !== 6. 逻辑运算符 先&& 后|| 7. 赋值运算符// 练习1: var s = 4 >= 6 || '人' != '阿凡达' && !(12 * 2 == 144) && true console.log(s); //true // 练习2: var num = 10; var f = 5 == num / 2 && (2 + 2 * num) console.log(f.toString() === 22) //false总结:操作符的使用,基本数学运算,一元运算符自增自减及前置后置的区别,逻辑运算符及取值,关系比较运算符,赋值运算符,运算符优先级;第5章 流程控制程序的三种基本结构顺序结构: 从上到下执行的代码就是顺序结构程序默认就是由上到下顺序执行的;分支结构:根据不同的情况及判断,执行对应代码;循环结构:重复执行一段代码;5.1 分支结构if语句语法结构if (/* 条件表达式 */) { // 执行语句 } if (/* 条件表达式 */){ // 成立执行语句 } else { // 否则执行语句 } if (/* 条件1 */){ // 成立执行语句 } else if (/* 条件2 */){ // 成立执行语句 } else if (/* 条件3 */){ // 成立执行语句 } else { // 最后默认执行语句 }案例//获取两个数字中的最大值 var num1=100; var num2=20; if(num1>num2){ console.log(num1); }else{ console.log(num2); }// 判断一个数是偶数还是奇数 var n = 10; if(n%2==0){ console.log('偶数'); }else{ console.log('奇数'); } /* * 例子: * 获取考试的分数,如果成绩是在90(含)分以上的,则显示级别:A * 如果成绩是大于等于80的则:B * 如果成绩是大于等于70的则:C * 如果成绩是大于等于60的则:D * 如果成绩是小于60的则:E * * */ var score = 91; if (score >= 90) { console.log("A"); } else if (score >= 80) { console.log("B"); } else if (score >= 70) { console.log("C"); } else if (score >= 60) { console.log("D"); } else { console.log("E"); }练习:判断一个年份是闰年还是平年闰年:能被4整除,但不能被100整除的年份 或者 能被400整除的年份var n = 2016; if(n%4==0){ if(n%100 !=0){ console.log('闰年'); }else if(n%400 ==0){ console.log('闰年'); }else{ console.log('平年'); } }else{ console.log('平年'); }三元运算符表达式1 ? 表达式2 : 表达式3 是对if……else语句的一种简化写法案例:// 是否年满18岁 var age = 18; var s = age>=18?'Yes':'no'; console.log(s);// 从两个数中找最大值 var a1 = 110; var a2 = 19; var s = a1>a2?a1:a2; console.log(s);switch语句语法格式:switch (expression) { case 常量1: 语句; break; case 常量2: 语句; break; … case 常量n: 语句; break; default: 语句; break; } /* * 执行过程: * 获取表达式的值,和值1比较,相同则执行代码1,遇到break跳出整个语句,结束 * 如果和值1不匹配,则和值2比较,相同则执行代码2,遇到break跳出整个语句,结束 * 如果和值2不匹配,则和值3比较,相同则执行代码3,遇到break跳出整个语句,结束 * 如果和值3不匹配,则和值4比较,相同则执行代码4,遇到break跳出整个语句,结束 * 如果和之前的所有的值都不一样,则直接执行代码5,结束 */break可以省略,如果省略,代码会继续执行下一个case switch 语句在比较值时使用的是全等操作符, 因此不会发生类型转换(例如,字符串'10' 不等于数值 10)/* * * 判断这个人的成绩的级别: * 如果是A,则提示,分数在90分以上 * 如果是B,则提示,分数在80分以上 * 如果是C,则提示,分数在70分以上 * 如果是D,则提示,分数在60分以上 * 否则提示,不及格 * */ var jiBie="B"; switch (jiBie){ case "A" : console.log("分数在90分以上的"); break; case "B" : console.log("分数在80分以上的"); break; case "C" : console.log("分数在70分以上的"); break; case "D" : console.log("分数在60分以上的"); break; default : console.log("不及格"); } 5.2 循环结构在JS语言中,循环语句有三种,while、do..while、for循环。5.2.1 while语句基本语法:// 当循环条件为true时,执行循环体, // 当循环条件为false时,结束循环。 while (循环条件) { //循环体 }案例1:计算1-100之间所有数的和// 初始化变量 var i = 1; var sum = 0; // 判断条件 while (i <= 100) { // 循环体 sum += i; // 自增 i++; } console.log(sum);案例2:打印100以内 7的倍数var i = 1; while(i<100){ if(i%7==0){ console.log(i); } i++; }案例3:打印100以内所有偶数var i = 1; while(i<=100){ if(i%2==0){ console.log(i); } i++; }案例4:打印100以内所有偶数的和var i = 1; var s = 0; while(i<=100){ if(i%2==0){ s = s+i; } i++; } console.log(s);练习:打印100以内的奇数打印100以内的奇数的和5.2.2 do...while语句do..while循环和while循环非常像,二者经常可以相互替代,但是do..while的特点是不管条件成不成立,都会执行一次。do { // 循环体; } while (循环条件);案例:计算1+2+3+4+……+99+100 的结果// 初始化变量 var i = 0; var sum = 1; do { sum += i;//循环体 i++;//自增 } while (i <= 100);//循环条件5.2.3 for语句while和do...while一般用来解决无法确认次数的循环。for循环一般在循环次数确定的时候比较方便for循环语法:// for循环的表达式之间用的是;号分隔的,千万不要写成, for (初始化表达式1; 判断表达式2; 自增表达式3) { // 循环体4 }执行顺序:1243 ---- 243 -----243(直到循环条件变成false)初始化表达式判断表达式自增表达式循环体//打印1-100之间所有数 for(var i=1;i<=100;i++){ console.log(i); } //求1-100之间所有数的和 var s = 0; for(var i=0;i<=100;i++){ s+=i; } console.log(s); //求1-100之间所有偶数的和 var s = 0; for(var i=1;i<=100;i++){ if(i%2==0){ s+=i; } } console.log(s); //打印正方形 var start = ''; for (var i = 0; i < 10; i++) { for (var j = 0; j < 10; j++) { start += '* '; } start += '\n'; } console.log(start); //打印直角三角形 var start = ''; for (var i = 0; i < 10; i++) { for (var j = i; j < 10; j++) { start += '* '; } start += '\n'; } console.log(start); //打印9*9乘法表 var str = ''; for (var i = 1; i <= 9; i++) { for (var j = i; j <=9; j++) { str += i + ' * ' + j + ' = ' + i * j + '\t'; } str += '\n'; } console.log(str);5.2.4 continue和breakbreak:立即跳出整个循环,即循环结束,开始执行循环后面的内容(直接跳到大括号)continue:立即跳出当前循环,继续下一次循环(跳到i++的地方)案例1:求1-100之间不能被7整除的整数的和(用continue)var s = 0; for(var i=0;i<100;i++){ if(i%7==0){ continue; } s+=i; } console.log(s);案例2:求200-300之间所有的奇数的和(用continue)var s = 0; for(var i=200;i<=300;i++){ if(i%2==0){ continue; }else{ s+=i; } } console.log(s);案例3:求200-300之间第一个能被7整数的数(break)for(var i=200;i<=300;i++){ if(i%7==0){ console.log(i); break; } }总结:代码的执行流程分为顺序、分支和循环三种结构,顺序结构是默认的,判断结构主要有if-else和switch-case两种,循环结构有while、do-while、for三种,其中continue和break是跳出循环;第6章 JS中特殊的对象-数组之前学习的数据类型,只能存储一个值(比如:Number/String)。我们想在一个变量中存储多个值,应该如何存储?所谓数组,就是将多个元素(通常是同一类型)按一定顺序排列放到一个集合中,那么这个集合我们就称之为数组。6.1 数组的创建// 字面量方式创建数组 var arr1 = []; //空数组 // 创建一个包含3个数值的数组,多个数组项以逗号隔开 var arr2 = [1, 3, 4]; // 创建一个包含2个字符串的数组 var arr3 = ['a', 'c']; console.log(arr1); console.log(arr2); console.log(arr3); // 可以通过数组的length属性获取数组的长度 console.log(arr3.length); // 可以设置length属性改变数组中元素的个数 arr3.length = 0; console.log(arr3[0]);//undefined数组的元素可以是任意类型的数据,因此,有时数组中的某个元素的值又是一个数组,而这样的数组被称为多维数组,如果数组中只有其他类型的数据,而没有另外的数组值,这样的数组被称为一维数组;通常,数组被嵌套N层,则称为N维数组,最常见的就是二维数组、三维数组、四维数组,超过一维的数组都会被泛称为多维数组;数组的维度值越大,复杂度就越高,开发中尽量避免产生高维度值的数组;var arr1 = [a,b,c]; // 一维数组 var arr2 = [a,b,c,[d,e]]; // 二维数组 var arr3 = [a,b,c,[d,e,[f,g]]]; // 三维数组 var arr4 = [a,b,c,[d,e,[f,g,[h,t,y]]]]; // 四维数组6.2 获取数组元素// 格式:数组名[下标] 下标又称索引 // 下标从0开始 // 功能:获取数组对应下标的那个值,如果下标不存在,则返回undefined。 var arr = ['red',, 'green']; arr[0]; // red arr[1]; // undefined 下标位置没有数据 arr[2]; // green arr[5]; // 这个数组的最大下标为2,因此返回undefined// 获取多维数组的数据 var arr = ['路飞','娜美',['巴基','女帝',['佐助','乔巴']]]; console.log(arr[2][2][0]); //佐助6.3 遍历数组遍历:遍及所有,对数组的每一个元素都访问一次就叫遍历。for循环数组遍历的基本语法:for(var i = 0; i < arr.length; i++) { // 数组遍历的固定结构 }for循环示例:var arr1 = [1, 3, 4]; for(var i = 0;i<arr1.length;i++){ console.log(arr1[i]); }whil循环示例:var arr1 = [1, 3, 4]; var i = 0; while(i<arr1.length){ console.log(arr1[i]); i++; }6.4 为数组修改添加元素// 格式:数组名[下标/索引] = 值; // 如果下标有对应的值,会把原来的值覆盖,如果下标不存在,会给数组新增一个元素。 var arr = ["red", "green", "blue"]; // 把red替换成了yellow arr[0] = "yellow"; // 给数组新增加了一个pink的值 arr[3] = "pink";6.5 数组操作案例案例1:求数组中的所有数的和//求和 var arr = [10, 20, 30, 40, 50]; //定义变量存储和 var sum = 0; for (var i = 0; i < arr.length; i++) { sum += arr[i]; } console.log("和为:" + sum);案例2:获取数组中的最大值//最大值 var arr = [10, 20, 30, 40, 50, 60]; //假设这个变量中的值是最大的 var maxNum = arr[0]; //遍历数组 for (var i = 0; i < arr.length; i++) { //判断 if (maxNum < arr[i]) { maxNum = arr[i]; } } console.log("最大值是:" + maxNum);案例3: 遍历出数组中所有的偶数// 遍历出数组中所有的偶数 var arr = [1,2,3,4,5,6,7]; for(var i=0;i<arr.length;i++){ //判断 if(arr[i]%2==0){ console.log(arr[i]); } }案例4:将数组转为字符串并以 | 分割//把数组中的每个名字后面拼接一个|然后以字符串的方式输出 var names = ["卡卡西", "佐助", "凤姐", "鸣人", "黑崎一护"]; var str = "";//空的字符串,用来存储最后的拼接的结果的字符串 //不停的遍历数组的数据,并且拼接字符串 for (var i = 0; i < names.length - 1; i++) { str += names[i] + "|";//拼接字符串的方式 } str += names[names.length - 1]; console.log(str);总结:数组就是多个数据的集合,有一维数组和多维数组之分,可以使用字面量方式创建数组,使用下标来获取数组元素数据,使用for或者while循环来遍历数组元素;第7章 函数把一段相对独立的具有特定功能的代码块封装起来,形成一个独立实体,就是函数,起个名字(函数名),在后续开发中可以反复调用函数的作用就是封装一段代码,将来可以重复使用7.1 函数的声明及调用7.1.1 声明关键字声明function 函数名(){ // 函数体 }表达式声明var fn = function() { // 函数体 }特点:函数声明的时候,函数体并不会执行,只要当函数被调用的时候才会执行。一个函数一般都特定的用来干 一件 事情7.1.2 调用调用函数的语法:函数名();特点:函数体只有在调用的时候才会执行,调用需要()进行调用。可以调用多次(重复使用)// 声明函数 function sayHi() { console.log("吃了没?"); } // 调用函数 sayHi(); // 求1-100之间所有数的和 function getSum() { var sum = 0; for (var i = 0; i < 100; i++) { sum += i; } console.log(sum); } // 一段代码可以多次调用 getSum(); getSum(); getSum();7.2 参数为什么要有参数function getSum() { var sum = 0; for (var i = 1; i <= 100; i++) { sum += i; } console.log(); } // 虽然上面代码可以重复调用,但是只能计算1-100之间的值 // 如果想要计算n-m之间所有数的和,应该怎么办呢?语法:// 函数内部是一个封闭的环境,可以通过参数的方式,把外部的值传递给函数内部 // 带参数的函数声明 function 函数名(形参1, 形参2, 形参...){ // 函数体 } // 带参数的函数调用 函数名(实参1, 实参2, 实参3);形参和实参* 形式参数:在声明一个函数的时候,为了函数的功能更加灵活,有些值是固定不了的,对于这些固定不了的值。我们可以给函数设置参数。这个参数没有具体的值,仅仅起到一个占位置的作用,我们通常称之为形式参数,也叫形参。实际参数:如果函数在声明时,设置了形参,那么在函数调用的时候就需要传入对应的参数,我们把传入的参数叫做实际参数,也叫实参。function fn(a, b) { console.log(a + b); } var x = 5, y = 6; fn(x,y); // x,y实参,有具体的值。 // 函数执行的时候会把x,y复制一份给函数内部的a和b, // 函数内部的值是复制的新值,无法修改外部的x,yJS 函数在调用时,允许传多个实参,就是实参个数可以比形参个数多;7.3 函数的返回值当函数执行完的时候,并不是所有时候都要把结果打印。我们期望函数给我一些反馈(比如计算的结果返回进行后续的运算),这个时候可以让函数返回一些东西。也就是返回值。函数通过return返回一个值返回值语法://声明一个带返回值的函数 function 函数名(形参1, 形参2, 形参...){ //函数体 return 返回值; } //可以通过变量来接收这个返回值 var 变量 = 函数名(实参1, 实参2, 实参3);返回值详解:* 如果函数没有显示的使用 return语句 ,那么函数有默认的返回值:undefined如果函数使用 return语句,那么跟在return后面的值,就成了函数的返回值如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined函数使用return语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说return后面的所有其他代码都不会再执行。7.4 函数相关的其它事情7.4.1 匿名函数与自调用函数匿名函数:没有名字的函数匿名函数如何使用:将匿名函数赋值给一个变量,这样就可以通过变量进行调用var fun1 = function(){ console.log(1); } fun1();匿名函数如果没有任何变量来表示它,那么就不能直接调用来执行,因此可以通过匿名函数的自调用的方式来执行(function () { alert(123); })();关于自执行函数(匿名函数自调用)的作用:防止全局变量污染。7.4.2 函数本身也是值 *function fn() {} console.log(typeof fn);函数作为参数因为函数也是一种值类型,可以把函数作为另一个函数的参数,在另一个函数中调用function f1(f){ f(); } function f2(){ console.log(2); } f1(f2); //2函数做为返回值因为函数是一种类型,所以可以把函数可以作为返回值从函数内部返回。function fn(b) { var a = 10; return function () { alert(a+b); } } var f = fn(5); f(); //15总结:函数是一段代码的封装,可重复多次运行,函数的声明有表达式声明和关键字声明,使用 ·函数名()· 的方式进行调用,调用时传入的参数为实参,声明时的参数时形参,函数使用return返回值,函数可以是没有名字的匿名函数,函数本身也可以当做值使用;第8章 作用域与JS代码的运行 *作用域:变量可以起作用的范围和区域8.1 全局变量和局部变量 *全局变量与全局作用域在任何地方都可以访问到的变量就是全局变量,全局变量所在的区域就是全局作用域局部变量只在固定的代码片段内可访问到的变量,最常见的例如函数内部的变量,就是局部变量。局部变量所在的区域就是局部作用域(函数作用域)不使用var声明的变量是全局变量,不推荐使用。 变量退出作用域之后会销毁,全局变量关闭网页或浏览器才会销毁8.2 变量提升console.log(a); // undefined var a = 2;console.log(a); // a is not defined变量提升定义变量的时候,变量的声明会被提升到作用域的最上面,变量的赋值不会提升。函数提升JavaScript解析器首先会把当前作用域的函数声明提前到整个作用域的最前面f(); function f(){ console.log(12); //12 }var f = 1; function f(){ console.log(12); //12 } // 由于函数提升在前,所以被变量声明替换了; // 执行阶段,变量被复制为1,不再是一个函数, f(); // f is not a function注:不管是普通变量还是函数,尽量不要出现重名;8.3 JS代码的运行 *console.log(s); //undefined var s = 2;JavaScript代码的执行是由浏览器中的JavaScript解析器来执行的。JavaScript解析器执行JavaScript代码的时候,分为两个过程:预解析(编译)过程和代码执行过程预解析过程:语法检查,如果有错误,直接停止后续步骤不再运行。把变量和函数的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值和调用。先提升变量后提升函数,如果函数和变量同名,则被替换;代码执行过程变量的赋值,函数的调用,循环判断等,根据代码由上往下顺序执行;var a = 25; function abc (){ alert(a);//undefined var a = 10; } abc(); // 如果变量和函数同名的话,函数优先做提升 console.log(a); function a() { console.log('aaaaa'); } var a = 1; console.log(a); // 1、---------------- var num = 10; fun(); function fun() { console.log(num); //undefined var num = 20; } // 2、---------------- var a = 18; f1(); function f1() { var b = 9; console.log(a); //undefined console.log(b); // 9 var a = '123'; }8.4 词法作用域变量的作用域是在定义时决定而不是执行时决定的,也就是说词法作用域取决于编译阶段,通过静态分析就能确定,因此词法作用域也叫做静态作用域。在 js 中词法作用域规则:函数允许访问函数外的数据.整个代码结构中只有函数可以限定作用域.作用域规则首先使用提升规则分析如果当前作用规则中有名字了, 就不考虑外面的名字var num = 123; function foo() { console.log( num ); } foo(); if ( false ) { var num = 123; } console.log( num ); // undefiend也就是说:函数内部可以访问函数外部的变量,但是函数外部不可以访问函数内部的变量;函数内部如果有变量,则优先使用内部的变量,如果函数内部没有,才会使用函数外部的变量;8.5 作用域链 *只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域, 即全局作用域。凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。 将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。var a = 1; function fn1(){ function fn2(){ function fn3(){ console.log(a); } fn3(); } fn2(); } fn1();var a = 1; function fn1(){ var a = 2; function fn2(){ var a = 3; function fn3(){ console.log(a); } fn3(); } fn2(); } fn1();总结:函数内部是JS代码的局部作用域,函数外部是全局作用域,JS 代码的运行分为与解析阶段和执行阶段,变量的声明实在与解析阶段的,所以变量存在提升,而变量只在自己的作用域中起作用,但是自己作用域是可以访问上级作用域的;第9章 对象(Object)9.1 什么是对象万物皆对象现实生活中:万物皆对象,对象是一个具体的事物,一个具体的事物就会有行为和特征。 举例: 一部车,一个手机 车是一类事物,门口停的那辆车才是对象 特征:红色、四个轮子 行为:驾驶、刹车9.2 JavaScript中的对象JavaScript中的对象其实就是生活中对象的一个抽象 JavaScript的对象是无序属性的集合。 其属性可以包含基本值、对象、数组或函数。 对象就是一组没有顺序的值。 我们可以把JavaScript中的对象想象成键值对,其中值可以是数据和函数。 对象的行为和特征 特征---属性 行为---方法事物的特征在对象中用属性来表示。事物的行为在对象中用方法来表示。属性和方法统称为对象的成员。9.3 如何得到一个对象字面量方式创建对象var obj1 = {};//得到一个空对象 var obj2 = {name:'张三',age:18};//得到拥有两个属性的对象 //得到拥有两个属性和一个方法的对象 var obj3 = { name:'张三', age:18, fei:function(){ console.log('你上天啊!'); } }new Object() 创建对象 (内置构造函数)var person = new Object(); person.name = 'lisi'; person.age = 35; person.sayHi = function(){ console.log('Hello,everyBody'); }自定义构造函数创建对象function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayHi = function(){ console.log('Hello,everyBody'); } } var p1 = new Person('张三', 22, 'actor');new关键字和构造函数构造函数 ,是一种特殊的函数,又叫做函数构造器。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。构造函数用于创建一类对象,首字母通常大写。构造函数要和new一起使用才有意义。new 关键字也读做实例化。实例化构造函数,得到一个对象。9.4 this 的指向JavaScript中的this指向问题,比较复杂,有时候会让人难以捉摸,随着学习的深入,我们会不断接触this,在学习过程中,我们可以不断总结,最终搞清楚this在何种情况下指向何处……目前,我们只需要记住以下两点就可以了:1: 函数如果在某个对象下,this就指向这个对象2: 函数如果被直接调用,this指向window对象var o1 = { name: '山治', f: function () { console.log(this.name); } } o1.f(); // 山治function f(){ console.log(this); } f(); console.log(window);9.5 对象的使用方法及属性的使用对象.方法名(); //调用对象的方法 对象.属性; //获取对象的属性遍历对象的属性通过for..in语法可以遍历一个对象var obj1 = { name:'路飞', age : 17, sex : '男', } for(var k in obj1){ console.log(k); console.log(obj1[k]); }注意:使用for …in语法,同样可以遍历数组注意:如果属性名或方法名,是一个变量,则使用对象[变量名] 语法删除对象的属性var obj1 = { name:'路飞', age : 17, sex : '男', } console.log(obj1.age); //17 delete obj1.age; //删除对象中指定的属性 console.log(obj1.age); // undefined总结:创建对象有三种方式,字面量、new内置构造函数及自定义构造函数;对象中有属性及方法,this指向当前对象,使用 . (点) 语法调用属性及方法;第10章 标准库对象(内置对象)JavaScript 提供了很多个内置对象:Math/Array/Number/String/Boolean...对象只是带有属性和方法的特殊数据类型。我们在学习时其实就是要记住对象的每个属性和方法怎么使用,代表什么含义;技术问题,遇到分歧,去哪里查找资料:火狐开发者社区--MDN微软开发者社区--MSDN10.1 Math对象https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math10.1.1 常用属性和方法Math 是一个内置对象, 它具有数学常数和函数的属性和方法。不是一个函数对象。与其它全局对象不同的是, Math 不是一个构造函数. Math 的所有属性和方法都是静态的.跟数学相关的运算直接使用Math中的成员即可console.log(Math.PI); //圆周率3.141592653589793 Math.random();//介于 0 和 1 之间的伪随机数。 Math.ceil(6.6);//获取大于或等于提供数值的最小整数--向上取整 Math.floor(8.8);//获取小于或等于提供数值的最大整数--向下取整 Math.round(9.9);//四舍五入 Math.max(10,20,15);//取多个值中的最大值 Math.min(10,20,15);//取多个值中的最小值 Math.pow(10,2);//返回x的y次幂 Math.sqrt(100);//求平方根 10.1.2 案例求10-20之间的随机数Math.floor(Math.random() * (max - min)) + min; 10.1.3 属性方法对照表Math对象的属性(常量)属性(常量)描述Math.E 常量.aspx)数学常数 e。这是欧拉数,自然对数的底。Math.LN2 常量.aspx)2 的自然对数。Math.LN10 常量.aspx)10 的自然对数。Math.LOG2E 常量.aspx)以 2 为底 e 的对数。Math.LOG10E 常量.aspx)以 10 为底 e 的对数。Math.PI 常量.aspx)Pi。这是圆的周长与直径的比值。Math.SQRT1_2 常量.aspx)0.5 的平方根,或相当于 1 除以 2 的平方根。Math.SQRT2 常量.aspx)2 的平方根。Math对象的方法(函数)方法(函数)描述Math.abs 函数.aspx)返回数字的绝对值。Math.acos 函数.aspx)返回数字的反余弦值。Math.acosh 函数.aspx)返回数字的双曲反余弦值(或反双曲余弦值)。Math.asin 函数.aspx)返回数字的反正弦值。Math.asinh 函数.aspx)返回数字的反双曲正弦。Math.atan 函数.aspx)返回数字的反正切值。Math.atan2 函数.aspx)将与 X 轴的角度(以弧度为单位)返回到由 y 和 x 坐标表示的点。Math.atanh 函数.aspx)返回数字的反双曲正切。Math.ceil 函数.aspx)返回大于或等于提供的数值表达式的最小整数。Math.cos 函数.aspx)返回数字的余弦值。Math.cosh 函数.aspx)返回数字的双曲余弦。Math.exp 函数.aspx)返回 e(自然对数的底)的乘幂数。Math.expm1 函数.aspx)返回 e(自然对数的底)的乘幂数减去 1 的结果。Math.floor 函数.aspx)返回小于或等于提供的数值表达式的最大整数。Math.hypot 函数.aspx)返回参数平方和的平方根。Math.imul 函数.aspx)返回被视为 32 位带符号整数的两个数字的积。Math.log 函数.aspx)返回数字的自然对数。Math.log1p 函数.aspx)返回 1 加上一个数字的的自然对数。Math.log10 函数.aspx)返回数字以 10 为底的对数。Math.log2 函数.aspx)返回数字以 2 为底的对数。Math.max 函数.aspx)返回提供的两个数值表达式中的较大值。Math.min 函数.aspx)返回提供的两个数字中的较小值。Math.pow 函数.aspx)返回基表达式的指定乘幂数的值。Math.random 函数.aspx)返回介于 0 和 1 之间的伪随机数。Math.round 函数.aspx)返回舍入到最近整数的指定数值表达式。Math.sign 函数.aspx)返回数字符号,它指示数字为正数、负数还是 0。Math.sin 函数.aspx)返回数字的正弦值。Math.sinh 函数.aspx)返回数字的反双曲正弦。Math.sqrt 函数.aspx)返回数字的平方根。Math.tan 函数.aspx)返回数字的正切值。Math.tanh 函数.aspx)返回数字的双曲正切。Math.trunc 函数.aspx)返回数字的整数部分,删除任何小数数字。10.2 Date对象(构造函数)https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date10.2.1 常用属性和方法创建 Date 实例用来处理日期和时间。Date 对象基于1970年1月1日(世界标准时间)起的毫秒数。// 获取当前时间,UTC世界时间,距1970年1月1日(世界标准时间)起的毫秒数 var now = new Date(); console.log(now.getTime()); // 获取距1970年1月1日(世界标准时间)起的毫秒数 console.log(now.valueOf()); // valueOf用于获取对象的原始值,与getTime()方法相同 Date构造函数的参数 1. 毫秒数 1498099000356 new Date(1498099000356) 2. 日期格式字符串 '2015-5-1' new Date('2015-5-1') 3. 年、月、日…… new Date(2015, 4, 1) // 月份从0开始获取日期的毫秒形式var now = new Date(); console.log(now.getTime()); // valueOf用于获取对象的原始值 console.log(now.valueOf()); // HTML5中提供的方法,有兼容性问题 var now = Date.now(); // 不支持HTML5的浏览器,可以用下面这种方式 var now = + new Date(); // 隐式调用 Date对象的valueOf() 日期格式化方法toString() // 转换成字符串 valueOf() // 获取毫秒值 // 下面格式化日期的方法,在不同浏览器可能表现不一致,一般不用 toDateString() toTimeString() toLocaleDateString() toLocaleTimeString()获取日期指定部分getTime() // 返回毫秒数和valueOf()结果一样,valueOf()内部调用的getTime() getSeconds() // 返回0-59 getMinutes() // 返回0-59 getHours() // 返回0-23 getDay() // 返回星期几 0周日 6周6 getDate() // 返回当前月的第几天 getMonth() // 返回月份,***从0开始*** getFullYear() //返回4位的年份 如 201610.2.2 案例案例1:写一个函数,格式化日期对象,返回yyyy-MM-dd HH:mm:ss的形式function formatDate(d) { //如果date不是日期对象,返回 if (!date instanceof Date) { return; } var year = d.getFullYear(), month = d.getMonth() + 1, date = d.getDate(), hour = d.getHours(), minute = d.getMinutes(), second = d.getSeconds(); month = month < 10 ? '0' + month : month; date = date < 10 ? '0' + date : date; hour = hour < 10 ? '0' + hour : hour; minute = minute < 10 ? '0' + minute:minute; second = second < 10 ? '0' + second:second; return year + '-' + month + '-' + date + ' ' + hour + ':' + minute + ':' + second; }计算时间差,返回相差的天/时/分/秒function getInterval(start, end) { var day, hour, minute, second, interval; interval = end - start; interval /= 1000; day = Math.round(interval / 60 /60 / 24); hour = Math.round(interval / 60 /60 % 24); minute = Math.round(interval / 60 % 60); second = Math.round(interval % 60); return { day: day, hour: hour, minute: minute, second: second } }10.2.3 方法对照表Date对象 的方法。方法描述getDate 方法.aspx)使用当地时间返回一个月某天的值。getDay 方法.aspx)使用当地时间返回一个星期某天的值。getFullYear 方法.aspx)使用当地时间返回年份值。getHours 方法.aspx)使用当地时间返回小时值。getMilliseconds 方法.aspx)使用当地时间返回毫秒值。getMinutes 方法.aspx)使用当地时间返回分钟值。getMonth 方法.aspx)使用当地时间返回月份值。getSeconds 方法.aspx)使用当地时间返回秒值。getTime 方法.aspx)将 Date 对象中的时间值返回为自 1970 年 1 月 1 日午夜起经过的毫秒数。getTimezoneOffset 方法.aspx)返回主机的时间与协调通用时间 (UTC) 之间的分钟差值。getUTCDate 方法.aspx)使用 UTC 返回一个月某天的值。getUTCDay 方法.aspx)使用 UTC 返回一个星期某天的值。getUTCFullYear 方法.aspx)使用 UTC 返回年份值。getUTCHours 方法.aspx)使用 UTC 返回小时值。getUTCMilliseconds 方法.aspx)使用 UTC 返回毫秒值。getUTCMinutes 方法.aspx)使用 UTC 返回分钟值。getUTCMonth 方法.aspx)使用 UTC 返回月份值。getUTCSeconds 方法.aspx)使用 UTC 返回秒值。getVarDate 方法.aspx)将 Date 对象中的 VT_DATE 值返回。getYear 方法.aspx)返回年份值。hasOwnProperty 方法.aspx)返回一个布尔值,该值指示一个对象是否具有指定名称的属性。isPrototypeOf 方法.aspx)返回一个布尔值,该值指示对象是否存在于另一个对象的原型链中。propertyIsEnumerable 方法.aspx)返回一个布尔值,该值指示指定属性是否为对象的一部分以及该属性是否是可枚举的。setDate 方法.aspx)使用当地时间设置一个月中某一日的数值。setFullYear 方法.aspx)使用当地时间设置年份值。setHours 方法.aspx)使用当地时间设置小时值。setMilliseconds 方法.aspx)使用当地时间设置毫秒值。setMinutes 方法.aspx)使用当地时间设置分钟值。setMonth 方法.aspx)使用当地时间设置月份值。setSeconds 方法.aspx)使用当地时间设置秒值。setTime 方法.aspx)设置 Date 对象中的日期和时间值。setUTCDate 方法.aspx)使用 UTC 设置一个月中某一日的数值。setUTCFullYear 方法.aspx)使用 UTC 设置年份值。setUTCHours 方法.aspx)使用 UTC 设置小时值。setUTCMilliseconds 方法.aspx)使用 UTC 设置毫秒值。setUTCMinutes 方法.aspx)使用 UTC 设置分钟值。setUTCMonth 方法.aspx)使用 UTC 设置月份值。setUTCSeconds 方法.aspx)使用 UTC 设置秒值。setYear 方法.aspx)使用当地时间设置年份值。toDateString 方法.aspx)以字符串值的形式返回一个日期。toGMTString 方法.aspx)返回使用格林尼治标准时间 (GMT) 转换为字符串的日期。toISOString 方法.aspx)以字符串值的形式返回采用 ISO 格式的日期。toJSON 方法.aspx)用于在 JSON 序列化之前转换目标类型的数据。toLocaleDateString 方法.aspx)将一个日期以字符串值的形式返回,该字符串应适合于宿主环境的当前区域设置。toLocaleString 方法.aspx)返回使用当前区域设置转换为字符串的对象。toLocaleTimeString 方法.aspx)以字符串值的形式返回一个时间,此字符串值应与宿主环境的当前区域设置相适应。toString 方法.aspx)返回表示对象的字符串。toTimeString 方法.aspx)以字符串值形式返回时间。toUTCString 方法.aspx)返回使用 UTC 转换为字符串的日期。valueOf 方法.aspx)返回指定对象的原始值。10.3 Array对象https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array10.3.1 常用属性和方法length属性: 返回数组的成员数量。var arr = ['a', 'b']; console.log(arr.length) // 2常用方法push方法用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组。var a = []; a.push(1) // 1 a.push('a') // 2 a.push(true, {}) // 4 console.log(a); //[1, 'a', true, {}]pop方法用于删除数组的最后一个元素,并返回该元素。注意,该方法会改变原数组var a = ['a', 'b', 'c']; a.pop() // 'c' console.log(a);// ['a', 'b']slice方法用于提取原数组的一部分,返回一个新数组,原数组不变。它的第一个参数为起始位置(从0开始),第二个参数为终止位置(但该位置的元素本身不包括在内)。 如果省略第二个参数,则一直返回到原数组的最后一个成员。var a = ['a', 'b', 'c']; a.pop() // 'c' console.log(a);// ['a', 'b']join方法用于将数组元素以指定字符拼接为字符串,返回一个字符串,原数组不变。var a = ['a','b','c','d','e']; console.log(a.join('-')) // 'a-b-c-d-e'返回数组的字符串表示形式。var arr = [1, 2, 3, 4]; console.log(arr.toString()); //1,2,3,410.3.2 方法和属性对照表Array 对象的属性。属性描述length 属性.aspx)返回一个整数值,此整数比数组中所定义的最高位元素大 1,是实际元素个数。Array 对象的方法。方法描述concat 方法(数组).aspx)返回由两个数组组合而成的新数组。entries 方法.aspx)返回包含数组的键/值对的迭代器。every 方法.aspx)检查定义的回调函数是否为数组中的所有元素返回 true。fill 方法.aspx)使用指定值填充数组。filter 方法.aspx)对数组的每个元素调用定义的回调函数,并返回回调函数为其返回 true 的值的数组。findIndex 方法.aspx)返回满足回调函数中指定的测试条件的第一个数组元素的索引值。forEach 方法.aspx)为数组中的每个元素调用定义的回调函数。hasOwnProperty 方法.aspx)返回一个布尔值,该值指示某个对象是否具有指定名称的属性。indexOf 方法(数组).aspx)返回某个值在数组中的第一个匹配项的索引。isPrototypeOf 方法.aspx)返回一个布尔值,该值指示某个对象是否存在于另一个对象的原型链中。join 方法.aspx)返回由一个数组的所有元素串联而成的 String 对象。keys 方法.aspx)返回包含数组的索引值的迭代器。lastIndexOf 方法(数组).aspx)返回指定值在数组中的最后一个匹配项的索引。map 方法.aspx)对数组的每个元素调用定义的回调函数并返回包含结果的数组。pop 方法.aspx)从数组中移除最后一个元素并将该元素返回。propertyIsEnumerable 方法.aspx)返回一个布尔值,该值指示指定属性是否为对象的一部分且是否可枚举。push 方法.aspx)将新元素追加到一个数组中,并返回数组的新长度。reduce 方法.aspx)通过对数组中的所有元素调用定义的回调函数来累积单个结果。 回调函数的返回值是累积的结果,并且作为对回调函数的下一个调用中的参数提供。reduceRight 方法.aspx)通过对数组中的所有元素调用定义的回调函数来按降序顺序累积单个结果。 回调函数的返回值是累积的结果,并且作为对回调函数的下一个调用中的参数提供。reverse 方法.aspx)将元素顺序被反转的 Array 对象返回。shift 方法.aspx)从数组中移除第一个元素并将返回该元素。slice 方法(数组).aspx)返回一个数组中的一部分。some 方法.aspx)检查定义的回调函数是否为数组的任何元素返回 true。sort 方法.aspx)返回一个元素已经进行了排序的 Array 对象。splice 方法.aspx)从一个数组中移除元素,如有必要,在所移除元素的位置上插入新元素,并返回所移除的元素。toLocaleString 方法.aspx)返回使用当前区域设置的字符串。toString 方法.aspx)返回数组的字符串表示形式。unshift 方法.aspx)在数组的开头插入新元素。valueOf 方法.aspx)获取对数组的引用。values 方法.aspx)返回包含数组的值的迭代器。10.4 String对象https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String10.4.1 常用属性和方法var s = 'JavaScript'; // length属性返回字符串的长度。 var i = s.length; //返回参数在字符串中第一次出现的位置 var i = s.indexOf('b'); //从原字符串取出子字符串并返回,不改变原字符串 var i = s.substr(2,4);//从下标第二个开始截取4个长度的字符串 //toLowerCase方法用于将一个字符串全部转为小写 //toUpperCase则是全部转为大写 var i = s.toLowerCase(); var i = s.toUpperCase(); // 用于替换匹配的子字符串,只替换第一个匹配 var i = s.replace('a','b'); console.log(i);10.4.2 方法和属性对照表String 对象的属性。属性描述constructor 属性.aspx)指定用于创建对象的函数。length 属性(字符串).aspx)返回 String 对象的长度。prototype 属性.aspx)为对象的类返回原型的引用。String 对象的方法。方法描述anchor 方法.aspx)将具有 NAME 特性的 HTML 定位点放置在文本两侧。big 方法.aspx)将 HTML 标记放置在文本两侧。blink 方法.aspx)将 HTML 标记放置在文本两侧。bold 方法.aspx)将 HTML 标记放置在文本两侧。charAt 方法.aspx)返回指定索引处的字符。charCodeAt 方法.aspx)返回指定字符的 Unicode 编码。codePointAt 方法.aspx)返回一个 Unicode UTF-16 字符的码位。concat 方法(字符串).aspx)返回由提供的两个字符串串联而成的字符串。EndsWith 方法.aspx)返回一个布尔值,该值指示字符串或子字符串是否以传入字符串结尾。includes 方法.aspx)返回一个布尔值,该值指示传入字符串是否包含在字符串对象中。fixed 方法.aspx)将 HTML 标记放置在文本两侧。fontcolor 方法.aspx)将具有 COLOR 特性的 HTML 标记放置在文本两侧。fontsize 方法.aspx)将具有 SIZE 特性的 HTML 标记放置在文本两侧。hasOwnProperty 方法.aspx)返回一个布尔值,该值指示某个对象是否具有指定名称的属性。indexOf 方法(字符串).aspx)返回字符串内第一次出现子字符串的字符位置。isPrototypeOf 方法.aspx)返回一个布尔值,该值指示某个对象是否存在于另一个对象的原型链中。italics 方法.aspx)将 HTML 标记放置在文本两侧。lastIndexOf 方法(字符串).aspx)返回字符串内子字符串的最后一个匹配项。link 方法.aspx)将具有 HREF 特性的 HTML 定位点放置在文本两侧。localeCompare 方法.aspx)返回一个值,该值指示两个字符串在当前区域设置中是否相等。match 方法.aspx)通过使用提供的正则表达式对象来搜索字符串并以数组形式返回结果。normalize 方法.aspx)返回指定字符串的 Unicode 范式。propertyIsEnumerable 方法.aspx)返回一个布尔值,该值指示指定属性是否为对象的一部分且是否可枚举。repeat 方法.aspx)返回一个新的字符串对象,它的值等于重复了指定次数的原始字符串。replace 方法.aspx)使用正则表达式替换字符串中的文本并返回结果。search 方法.aspx)返回正则表达式搜索中第一个子字符串匹配项的位置。slice 方法(字符串).aspx)返回字符串的片段。small 方法.aspx)将 HTML 标记放置在文本两侧。split 方法.aspx)返回一个字符串拆分为若干子字符串时所产生的字符串数组。StartsWith 方法.aspx)返回一个布尔值,该值指示字符串或子字符串是否以传入字符串开头。strike 方法.aspx)将 HTML 标记放置在文本两侧。sub 方法.aspx)将 HTML 标记放置在文本两侧。substr 方法.aspx)返回一个从指定位置开始且具有指定长度的子字符串。substring 方法.aspx)返回 String 对象中指定位置处的子字符串。sup 方法.aspx)将 HTML 标记放置在文本两侧。toLocaleLowerCase 方法.aspx)返回一个字符串,其中所有字母字符都转换为小写形式,并将考虑主机环境的当前区域设置。toLocaleString 方法.aspx)返回使用当前区域设置转换为字符串的对象。toLocaleUpperCase 方法.aspx)返回一个字符串,其中所有字母字符都转换为大写形式,并将考虑主机环境的当前区域设置。toLowerCase 方法.aspx)返回一个字符串,其中所有字母字符都转换为小写形式。toString 方法.aspx)返回字符串。toUpperCase 方法.aspx)返回一个字符串,其中所有字母字符都转换为大写形式。trim 方法.aspx)返回已移除前导空格、尾随空格和行终止符的字符串。valueOf 方法.aspx)返回字符串。10.5 包装对象对象是 JavaScript 语言最主要的数据类型,三种原始类型的值——数值、字符串、布尔值——在一定条件下,也会自动转为对象,也就是原始类型的“包装对象”。所谓“包装对象”,就是分别与数值、字符串、布尔值相对应的Number、String、Boolean三个原生对象。这三个原生对象可以把原始类型的值变成(包装成)对象。var v1 = new Number(123); var v2 = new String('abc'); var v3 = new Boolean(true); typeof v1 // "object" typeof v2 // "object" typeof v3 // "object" v1 === 123 // false v2 === 'abc' // false v3 === true // false包装对象的最大目的,首先是使得 JavaScript 的对象涵盖所有的值,其次使得原始类型的值可以方便地调用某些方法。原始类型的值,可以自动当作对象调用,即调用各种对象的方法和参数。这时,JavaScript 引擎会自动将原始类型的值转为包装对象实例,在使用后立刻销毁实例。比如,字符串可以调用length属性,返回字符串的长度。'abc'.length // 3上面代码中,abc是一个字符串,本身不是对象,不能调用length属性。JavaScript 引擎自动将其转为包装对象,在这个对象上调用length属性。调用结束后,这个临时对象就会被销毁。这就叫原始类型与实例对象的自动转换。注意:JS的内置对象还有很多,我们只不过是学习了比较常用的几个而已;在后面的学习中,我们还有讲解使用其他类型的内置对象;可以查看狐火和微软开发者社区,获取更多知识……课外知识:JS代码规范&编程风格缩进:空格和tab键都可以,尽量保持一致,使用一种; 两个空格和四个空格都行,尽量保持一致就行,但是使用4个空格的多一些; 分号:尽量不要忘记,每一行的结束都要加分号 while 与 for 循环后面不要加分号 if else、switch等分支语句后面不要加分号 关键字声明函数,后面不要加分号 表达式声明函数,函数后面加分号 区块:两种写法if(){ } if() { } 理论上两种都可以,但是尽量使用第一种,因为js会在行尾自动添加分号,有时会出现意外情况;圆括号函数的声明和调用、表达式运算1:函数调用时,函数名和括号之间没有空格2:函数声明时,函数名和括号之间没有空格3:参与表达式运算时,括号的前面和后面,都要加空格变量的声明console.log(x); var x = 10; //等价于 var x; console.log(x); x = 10;为了避免此种情况的出现,建议将所有在本作用域下声明的变量都提到最前面声明并赋值;自增自减运算因为 ++ 在前和++ 在后的运算结果不同,所以,尽量使用 +=1 -=1 替代,提高代码的可读性;你的团队中一定有搞不明白++在前和在后的区别的二傻子;生活不易,请善待他们;赋值 =赋值前后加空格;变量命名和代码缩进 规范,是一个程序员必备的基本编程素质;让别人给你调试BUG的第一前提条件就是 缩进要规范;
2022年10月13日
30 阅读
0 评论
0 点赞
2022-09-19
清除浮动,单行多行超出用...
清除浮动,单行多行超出用...
2022年09月19日
295 阅读
0 评论
4 点赞
1
...
4
5