JavaScript百炼成仙 作者:剽悍一小兔
没事儿的时候多读书还是蛮好的,这本书给我的感觉就像是真的在看修仙人士不断修炼去打怪升级一样,以不一样的视角重新温习了一遍前端的知识,和以往所读的其他书籍有些不同,看此书的时候心态就很放松,还可以学到以前以前遗漏的知识,读了之后很想看该书后续书籍,个人觉得很值得阅读。
引言:目标和结果自己要分清楚。
一、JavaScript基础
1、声明变量
代码语言:javascript复制<script>
var a ;
a= "你好,JavaScript!";
var b = '你好,世界!';
console.log('使用双引号输出变量a:' a);
console.log('使用单引号输出变量b:',b);
var num = 15;
console.log("num=" num)
</script>
2、数据类型
数据类型分为原生数据类型(primitive type)和对象数据类型(object type)
原生数据类型包括
代码语言:javascript复制数字
字符串
布尔值
null
undefined
详细说明:
null和undefined都代表了直接量的空缺
undefined:定义了变量,却没有赋值
null:把某些变量赋值null,达到清空的目的
代码语言:javascript复制拓展:直接量
直接量包含:
数值
布尔值
字符串
null
undefined
对象
函数
null是一个特殊的关键字,表示没有值,null也是一个原始值,因为JavaScript是大小写敏感的,
所以null和Null、NULL或者其他变 量是有区别的。
undefined是一个顶级属性,它代表某一个变量未定义。同样, undefined也是一个原始值。
代码语言:javascript复制变量
变量就是先定义,然后通过赋值运算符"=",完成变量的绑定
举例子,当然了,不写var也可以,为了规范,强烈建议写上
代码语言:javascript复制var flag = true;
console.log("flag=" flag) // true
var flag1;
console.log("flag1=" flag1) // undefined
var flag2 = null;
console.log("flag2=" flag2) // null
console.log(10/20) // 0.5
console.log(5/2) // 2.5
代码语言:javascript复制拓展:JavaScript数据类型
6种数据类型
string
number
boolean
object
function
symbol
3种对象类型
Object
Date
Array
2个不包含任何值的数据类型
null
undefined
类型转换:
Number() 转换为数字 示例:var b = Number.parseInt("188") 还有parseFloat
String() 转换为字符串
Boolean() 转换为布尔值。
使用类型转换的时候:parseInt 和 parseFloat 可以直接使用
console.log(parseInt("888")) // 888
console.log(parseFloat("888.88")) // 888.88
3、对象数据类型
代码语言:javascript复制创建对象的方式
使用大括号{}去创建对象,访问对象中的值的话,通过对象名点属性名的方式
如果访问的键不存在,则返回undefined
var container ={
caoyao : "草药"
}
console.log(container.caoyao) // 取值
var con={}
con.wuqi="断念剑"
console.log(con.wuqi)
console.log(con.duyao) // undefined 不存在的键
对象的取值
对象不仅可以使用点的方式去访问
也可以使用中括号的方式,中括号里面可以写一个变量,写字符串也可以
con.mv="侄儿"
var prop = "mv"
console.log(con[prop]) // 侄儿
4、循环
代码语言:javascript复制for循环
for (let i = 0; i < 10; i ) {
console.log(i)
}
详解:
小括号里面有三个表达式
1、let i = 0 循环开始之前就执行,定义变量
2、i < 10 进入循环体的条件
循环体:大括号括起来的部分
3、i 控制循环遍历
输出的结果为:0到9
for (let i = 10; i > 0; i--) {
console.log(i)
}
详解:
同样是循环10次,变量i从10减到0的过程。
输出的结果为:10到1
求和:从1 到100
var sum=0;
for (let i = 0;i<101;i ){
sum =i;
}
console.log("sum=" sum) // 5050
也可以这样写:
var sum=0;
var i=0;
for (;i<101;){
sum =i;
i
}
console.log("sum=" sum) // 5050
代码语言:javascript复制while循环
var sum=0;
var i=0;
while (i<101){
sum =i
i
}
console.log(sum) // 5050
代码语言:javascript复制补充:i 和 i 的理解
i 是自增运算符,表示把当前的变量自增一个单位。(先运算、后自增)
i 是先自增一个单位,然后在运算。(先自增、后运算)
不论是 i 还是 i ,只要执行完毕,i的值都是自增。
代码语言:javascript复制for循环和while循环语法
for循环:
for(语句1; 语句2; 语句3){
被执行的代码块
}
语句1:在循环(代码块)开始前执行
语句2:定于运行循环(代码块)的条件
语句3:在循环(代码块)已被执行之后执行
while循环:会在指定条件为真时循环执行代码块
while(条件){
需要执行的代码
}
5、对象内容的遍历
使用typeof来查看JavaScript变量的数据类型
现在需要判断一个对象中哪些是属性哪些是函数,使用typeof关键字
案例:
代码语言:javascript复制 var n1 = 13;
var s1 = "abc"
function fun() {
}
console.log(typeof(n1)) // number
console.log(typeof(s1)) // string
console.log(typeof(fun)) // function
那么,该如何遍历一个对象呢?
代码语言:javascript复制 var person = {
name : "张三",
age : 18,
eat : function () {
console.log("爱吃KFC")
}
}
for (var p in person){
console.log(p,"值:" person[p])
}
小结:遍历对象时使用中括号的方式,因为不知道对象中的变量究竟是属性还是方法,所以使用中括号更加灵活。
6、JavaScript运算符
代码语言:javascript复制运算符:加、减、乘、除、赋值、取余(也称求余)、自增、自减
例如:定义了变量,却没有赋值,输出就是undefined
var a;
赋值:=
求余:%
自增:
自减:--
取余的意思是:一个数字除以另一个数字,除不尽的部分就是余数
例如:5除以2 余数为1
自增分为前置 和后置 ,自减也同理
关于自增和自减的一个练习:
var a = 1;
var b;
var sum = (b = a --a) a-- b ;
答案:5 拆分理解:sum = ( b = 1 1) 1 2
7、数组
数组是一个容器,可以存放一个或者多个对象。
创建数组的四种方式:
代码语言:javascript复制1、直接量定义数组:使用一对中括号声明一个数组对象,在定义的时候直接对数组进行初始化 (常用)
var arr = ["apple","banana","orange"];
console.log(arr)
2、采用构造函数:new Array(); 这里是直接创建一个空的数组
var arr = new Array();
arr.push("aaa")
arr.push("bbb")
console.log(arr)
3、采用构造函数:创建的时候设置初始长度
var arr = new Array(5);
arr[0]="张三" // 对数组进行赋值
arr[1]="李四"
console.log(arr) // ['张三', '李四', empty × 3]
console.log(arr.length) // 5
4、采用构造函数:创建的时候赋予元素
var arr = new Array("比亚迪","宝马","奔驰","大众");
console.log(arr)
数组方法:
代码语言:javascript复制push方法:在数组的尾部添加新元素
删除:splice 或 pop
pop方法:删除数组的最后一个元素
splice方法:可插入、删除、替换数组元素 (在原有的数组上进行修改)
splice用前俩个参数进行定位,余下的参数表示插入部分
参数1:数组的下标位置
参数2:要删除元素的个数
示例:
var nums = [1,2,3,4,5];
nums.splice(2,1); // 删除 3 号元素
console.log(nums) // [1, 2, 4, 5]
演示splice的插入元素:
var nums = [1,2,3,4,5];
nums.splice(2,1,38,66); // 把3替换为 38 在38后面添加一个元素 66
console.log(nums) // [1, 2, 38, 66, 4, 5]
元素转字符串:join方法
join是把所有元素放入一个字符串,元素是通过指定分隔符进行分隔的
var str = nums.join(","); // 也可以使用其他的 -- 555 等等进行分隔
console.log(str) // 1,2,38,66,4,5
代码语言:javascript复制拓展:数组的其他方法
concat方法:
将俩个数组或者元素之间连接起来,调用该方法的数组放在前面,方法实参放在后面
var arr1=[1,2,3]
var arr2=[4,5,6]
var arr3 = arr1.concat(arr2)
console.log(arr3) // [1, 2, 3, 4, 5, 6]
pop方法的补充:删除数组中的最后一个元素,切会将这个被删除的元素返回
var del1 = arr3.pop()
console.log("被删除的元素是:" del1) // 6
reverse方法:将数组的顺序反转
var arr4 = arr3.reverse()
console.log(arr4) // [6, 5, 4, 3, 2, 1]
shift方法:删除数组的第一项,并返回删除元素的值(和pop进行对比学习)
arr4.shift()
unshift方法:将元素添加到数组开头,并返回数组的长度(和push进行对比学习)
arr4.unshift(115)
slice方法:截取数组的元素,左闭右开(只有一个参数的时候,从当前位置到最后一个元素)
console.log(arr4) // [115, 118, 4, 3, 2, 1]
console.log("截取数组")
var arr5 = arr4.slice(1,3) // 118,4
var arr6 = arr4.slice(1) // [118, 4, 3, 2, 1]
map方法:映射---指数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
var arr7 = [1,2,3,4,5];
var arr8=arr7.map(function (x) {
return x*0.9;
})
console.log(arr8) // [0.9, 1.8, 2.7, 3.6, 4.5]
filter方法:过滤---数组中的每一项运行给定函数,返回满足过滤条件组成的数组。
var arr9=arr7.filter(function (x,index) {
return x%3==0 || index>3;
})
console.log(arr9) // 3,5
foreach方法:对数组进行遍历循环,对数组中的每一项运行给定函数。这个方法没有返回值。
参数都是function类型,默认有传参,参数分别为:
遍历的数组内容
第对应的数组索引
数组本身
arr7.forEach(function (x,index,arr) {
console.log(x ' | ' index 't' arr) // 1 | 0 1,2,3,4,5 一直到 5 | 4 1,2,3,4,5
})
every方法:判断数组中每一项都是否满足条件,只有所有项都满足条件,才会返回true。
var arr10=arr7.every(function (x) {
return x<10;
})
console.log(arr10) // true
some方法:判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回true。
var arr11=arr7.some(function (x) {
return x<2;
})
console.log(arr11) // true
8、函数七重关
代码语言:javascript复制一、函数定义
第一种方式:使用function关键字,后面是方法名,然后函数体
// 定义函数
function test1() { // 函数体
document.write("My Page")
}
test1(); // 调用函数
第二种方式:定义一个变量,把函数赋值给变量
var fun1=function () {
console.log("定义函数的另一种方式")
}
fun1()
区别:第二种方式必须先定义后调用
代码语言:javascript复制JavaScript编译原理
var a = 10;
JavaScript代码在运行之前会经过一个编译的过程,而编译有三个步骤。
1、分词
分词的目的是把这些代码分解为一个个代码块
比如刚才的例子,如果经过分词的步骤,那么得到的结果就是var、a、=、2、;。
2、解析
由JavaScript编译器对刚才分词得到的一个个 代码块进行解析,生成一棵抽象的语法树(AST)。
抽象语法树定义了代码本身,通过操作这棵树可以精准地定位到 赋值语句、声明语句和运算语句。
抽象语法树的创建可以在此网站自行调试和验证。https://esprima.org/demo/parse.html
3、代码生成
JavaScript引擎会把在第二个步骤中生成的抽象语法树进行转换,转换成 什么呢?没错,就是可执行的代码。
代码语言:javascript复制二、作用域
作用域:
全局作用域
函数作用域
作用域:就是当你要查找某一个变量的时候,你可以在什么范围呢找到这个变量。这个寻找的范围就是作用域。
不论是全局作用域还是函数作用域,都被定义在词法阶段。
词法阶段就是JavaScript代码编译的第一个步骤--分词。
全局作用域包着一个函数作用域,在函数作用域里面可以访问全局作用域里面的变量。但是反过来的话,全局作用域想要
调用函数作用域中定义的变量却是不可以的。
因此:当发生作用域嵌套的时候,只能里面的访问外面的。
示例:
var five = "星期五"
function five1() {
console.log("全局变量:" five)
}
five1()
但是下面这种形式就不是作用域嵌套,因为变量没有定义在函数体中
if(true){
var six = "星期六"
}
console.log("测试if中的变量:" six) // 测试if中的变量:星期六
这里并没有出现函数,所以不算作用域嵌套。
if(false){
var seven = "星期六"
}
console.log("测试if中的变量:" seven) // 测试if中的变量:undefined
这里undefined是因为代码在分词阶段和解析阶段,变量seven依然会被获取,系统默认给undefined值。
又因为变量seven没有在某一个函数体中,而是在全局作用域中,所以console.log方法可以访问这个变量。
加深理解:
var a = 1;
function test() {
var a;
var inner = function () {
console.log(a) // undefined
}
inner();
}
test();
解析:函数作用域里面嵌套了函数作用域,那么在最里面的inner函数中访问一个变量,就会优先在inner函数里面
寻找,结果却发现找不到。既然在当前函数作用域里面找不到,那么就往上翻一层,在它的父级作用域,也就是test
函数的作用域里面寻找,结果发现找到了。test函数里面定义了一个变量a,但是没有赋值,那么a 就是undefined。
既然已经找到了,那么就不会去全局作用域里面寻找变量a了。所以,全局作用域里面的变量a其实就是一个摆设。
代码语言:javascript复制三、参数传递
参数传递:就是函数调用的时候传进来的值
示例:
function add(a,b,c) {
console.log(arguments) // 函数的内部还维护了一个arguments数组 传多个参数都在此数组中
var sum = a b c;
console.log(sum)
}
add(1,2,3) // 6 若是传字符串的话会进行拼接
add(1) // NaN a的值是1,b和c的值就是undefined 结果为NaN 代表无法计算
实现多个数字的累加:
function add() {
if(!arguments[0]){ // 如果不存在
return 0;
}
for (var i=1;i<arguments.length;i ){
arguments[0]=arguments[0] arguments[i]
}
console.log(arguments[0])
}
add(1,2,3,4,5) // 15
方式不唯一:调用函数返回一个结果
function add() {
var sum=0;
for (var i=0;i<arguments.length;i ){
sum =arguments[i]
}
return sum;
}
var sum= add(1,2,3,4,5,6,7,8,9,10)
console.log(sum) // 55
只传一个参数案例:
function fun(a) {
console.log(a)
}
fun() // undefined
// 进行拆分
function fun() {
var a;
console.log(a)
}
这个参数也属于函数作用域,并没有给这个变量赋值,所以值是undefined
代码语言:javascript复制四、闭包
产生闭包的条件:
1、函数内部有一个函数
2、函数内部的函数里面用到了外部函数的局部变量
3、外部函数把内部函数作为返回值return出去
示例:
function test() {
var a = 100;
return function () {
console.log(a)
}
}
test()() // 第一个括号调用test函数,第二个括号才调用内部函数
闭包的好处:
正常情况下,我们调用一个函数,其里面的局部变量会在函数调用结束后销毁,这也是我们在全局作用域里面无法访
问函数局部变量的原因。
但是,如使用了闭包,那么就会让这个局部变量不随着原函数的销毁而销毁,而是继续存在。
比如:我们反复调用这个内部函数,会发现这个变量a一直存在,就好像是一个全局作用域里面的变量。
示例:
function test() {
var a = 100;
return function (increment) {
a=a increment
console.log(a)
}
}
var inner = test(); // 先获取内部函数
inner(1) // 101
inner(1) // 102
inner(1) // 103
利用这种写法就相当于在全局作用域里面定义了一个变量a,然后在函数中操作全局变量。
这种闭包的形式操作可以减少很多不必要的全局变量。
全局作用域是一块公共区域,如果为了某个单一的功能而定义一个全局变量,则会导致全局变量过多,代码较乱!
这种情况,优先考虑使用闭包。
代码语言:javascript复制五、自执行函数
自执行函数:定义之后就立刻执行的函数。
一般是没有名字的,正因为没有名字,所以它虽然会被立即执行,但是只会被执行一次。
语法:(定义一个没有名字的函数)();
示例:
(
function() {
console.log(12345)
}
)
();
自执行函数一般和闭包配合使用
function test() {
var a = 10;
return function (increment) {
a = a increment;
console.log(a)
}
}
var inner = test();
inner(1) // 11
inner(1) // 12
inner(1) // 13
修改之后:其实真正想要的是test里面的内部函数,不需要知道外部这个函数叫什么,叫什么都无所谓的。
var inner = (function () {
var a = 10;
return function (increment) {
a = a increment;
console.log(a)
}
})();
inner(2) // 12
inner(2) // 14
inner(2) // 16
inner() // NaN
外部函数只是为了产生闭包环境而临时定义的函数,因此,没必要给外部函数取名字!
代码语言:javascript复制六、new 一个函数
this永远指向当前函数的调用者
在全局作用域里面的任何东西,不管是变量还是函数,都属于windows对象。
示例:
function hello() {
console.log(this)
}
var a = 10;
console.log(window.a) // 10
hello()
window.hello() // 效果等同
对象可以通过俩种方式调用里面的属性:
1、点的方式 像 window.hello() 的形式
2、使用中括号,对象[属性名称] 属性名称可以是字符串也可以是变量
形式一:
window['hello']()
形式二:
var p = 'hello'
window[p]();
hello函数内部产生了一个新的对象,也就是hello函数的真实调用者
var hello1 = new hello();
console.log(hello1)
这种函数还有一个别称:构造函数,通过构造函数构造一个对象模板。
对象模板:指用一个函数的形式设计一种对象的种类。
示例:
// 使用函数的形式
function Fruit(name,color,smell) {
this.name=name
this.color=color
this.smell=smell
}
var apple= new Fruit('苹果','绿色','甜的')
console.log(apple) // Fruit {name: '苹果', color: '绿色', smell: '甜的'}
// 创建对象的一种形式
var apple2 = {
name:'苹果',
color:'绿色',
smell:'不甜'
}
console.log(apple2) // {name: '苹果', color: '绿色', smell: '不甜'}
除了基本数据类型之外,其他都引用数据类型。
对象就属于引用数据类型。
代码语言:javascript复制七、回调函数
回调函数:把一个函数的定义当做参数传递给另一个函数。
正常情况下,函数传参可以是一个数字,也可以是一个字符串,这都没问题。
但是JavaScript提供了一种强大的特性:函数也可以作为另一个函数的参数。
示例:
传统的函数:
function eat(food,howToEat,tiaoliao) {
alert(tiaoliao " ," howToEat "吃" food)
}
eat("羊肉串","笑嘻嘻地","撒上一撮孜然")
修改之后:使用回调函数
// callback 就是那个函数 执行eat的时候,直接执行callback,并且把food传入callback
// 在调用eat的时候 通过临时编写一个新的函数实现的
function eat(food,callback) {
callback(food)
}
eat('羊肉串',function (food) {
alert("笑嘻嘻地,撒上一撮孜然,开心的吃" food)
})
二、基础考核
1、变量和简单数据类型
代码语言:javascript复制简单数据类型:
字符串、数值、布尔、null、undefined
关于函数的说明:
函数之修,第一要义便是理解返回值、 参数列表和函数体。可以把函数想象成一个“黑盒子”,所谓参数,就是丢到
这个黑盒子中的物体,可以是单个物体,也可以是多个物体。黑盒子的内部空间就是函数的函数体!在函数体中,可以
对丢进来的参数进 行处理,处理结束后,如果该函数的设计者认为需要将得到的某个结果 从黑盒子中扔出,作为对于
“黑盒子访问者”的奖赏,就可以使用return 关键字将其抛出。
函数之修,第二要义便是业务逻辑,作为函数的设计者,必须清 楚地明白自己设计这个函数是为了什么。叶小凡
眼中一亮,想到了刚才 化浮为整的过程,因为0.1和0.2都是小数,所以直接相加会产生精度丢失的问题。
要知道:方法就是函数,函数的调用需要括号。
indexOf方法:在原字符串中搜索一个特定的字符串,将目标字符串在原字符串中的位置返回。(下标从0开始)
查询一个不存在的匹配项则返回 -1
replace方法:字符串替换,参数1:需要替换的内容 参数2:替换后的内容
它只能匹配到字符串中的第一个匹配项。
俩数求和可能会遇到的问题
// 计算精度问题 加法和乘法都有这个问题
console.log(0.1 0.2) // 0.30000000000000004
console.log(1.1 1.122) // 2.2220000000000004
console.log(1.001*1000) // 1000.9999999999999
console.log(1 1.02) // 2.02 正常
console.log(301/100) // 3.01 正常
计算俩数之和:
// 计算俩数之和
function add(num1,num2) {
// 将数字转换为字符串
num1 = num1.toString();
num2 = num2.toString();
// 获取小数点的位置
var index1 = num1.indexOf(".")
var index2 = num2.indexOf(".")
// 如果小数点存在 那么就在获取各自的小数位数
var ws1 = 0;
var ws2 = 0;
if(index1 != -1){
ws1 = num1.split(".")[1].length
}
if(index2 != -1){
ws2 = num2.split(".")[1].length
}
// 看谁的小数位数大 谁的小数位数小
var bigger = (ws1>ws2)?ws1:ws2
var smaller = (ws1<ws2)?ws1:ws2
// 计算得到需要补齐的0的个数
var zeroCount = bigger - smaller
// 去除小数点
num1 = num1.replace(".","")
num2 = num2.replace(".","")
// 比较num1和num2 谁大 比较方法就是看谁是smaller 是smaller的就补0
if(ws1==smaller){
for (var i = 0;i < zeroCount; i ){
num1 = "0"
}
}else {
for (var i = 0;i < zeroCount; i ){
num2 = "0"
}
}
// 开始计算
var sum = parseInt(num1) parseInt(num2)
// 根据较大的小数位数计算倍数
var times = 1;
for (var i = 0; i< bigger; i ){
times = times*10
}
sum = sum/times
return sum
}
console.log(add(1.01,1.222)) // 2.232
console.log(add(0.1,0.2)) // 0.3
console.log(add(1,0.2)) // 1.3
三、jQuery和DOM
代码语言:javascript复制jQuery简介
jQuery封装了 JavaScript常用的功能代码,提供了简便的JavaScript API,优化了HTML 文档操作、事件处理
动画设计和Ajax交互。
jQuery的核心特性可以总结为:具有独特的链式语法和短小清晰的 多功能接口;具有高效灵活的CSS选择器进行扩展
拥有便捷的插件扩 展机制和丰富的插件。
源生JavaScript获取dom元素:
document.getElementById("id的值")
使用jQuery获取dom元素:
$('#id的值')
jQuery支持ajax异步无刷新技术
基于jQuery的ajax方法:
$.ajax({
url: '', // 后台请求地址
type: '', // 请求方式 get post
dataType: '', // 接受数据的方式 text json (json 可不写)
data: { // 为后台传递的参数
},
success: function (data) { // 回调函数 后台执行完毕 该函数才执行
// data是responseText,是jQuery处理后的数据
},
error: function () { // 表示当请求发生错误的时候 才执行
}
})
代码语言:javascript复制拓展:页面加载即执行jQuery的三种方式
1、$(function(){ ... }) // 推荐使用
例如:
<p id="name"></p>
$(function () {
$('#name').html("大哥");
})
2、 $(document).ready(function(){ ... })
是第一种形式的完整形式
3、 window.onload = function(){ ... }
记得引入cdn
代码语言:javascript复制jQuery选择器
$("#id") id选择器
$(".class名") class选择器
$("标签名") 标签选择器
$("#id, .class, 标签名") 组合选择器
$("*") 选取当前页面中所有dom对象,通配符选择器
书中有提及:
群组选择器:可以理解为组合选择器,多了class样式之间是有逗号隔开
后代选择器:如果一个标签里面嵌套了另外一个标签,那么里面的标签可以认 为是外面标签的后代。
示例:
<style>
.outer{
height: 300px;
weight: 200px;
border: 3px solid yellow;
background: wheat;
}
.outer .inner{
display: block;
height: 100px;
weight: 50px;
background: pink;
}
</style>
<div class="outer">
<span class="inner">inner1</span>
</div>
其他案例:例如 点击事件
<input type="button" value="点击事件" id="btn">
<script>
$(function () {
$("#btn").click(function () {
alert("点击事件触发了!!")
})
})
</script>
关于jQuery选择器可以参考:jQuery选择器参考手册
代码语言:javascript复制使用jQuery操作dom
示例:
<h3>法宝列表</h3>
<ul>
<li id="a1">天马妖焰钟</li>
<li id="a2">流魂歧霞伞</li>
<li id="a3">天网奇火石</li>
<li id="a4">混沌秘霖扇</li>
<li id="a5">昊天粗琉斧</li>
</ul>
<input type="button" value="获取第二件法宝" id="magic">
1、查找元素
要求:获取第二件法宝
$(document).ready(function () {
$("#magic").click(function () {
var ul = $("ul");
var lis = ul.find("li");
var li = lis.eq(1); // 下标是从0开始的
alert(li.text())
})
})
也可以这样写:这个方法是直接使用后代选择器选择所有li元素,然后用eq方法比较即可
window.onload = function () {
$("#magic").click(function () {
var text = $('ul li:eq(1)').text();
// var text = $('ul li').eq(1).text(); // 效果是等同的
alert(text);
})
}
2、查找属性
要求:寻找最后一个li元素的id属性值
// 这里的意思是 获取第四个元素的id属性 固定了li 的个数
var id = $("ul li").eq(4).attr("id");
alert(id) // a5
// 这里写了俩次ul li 有点重复
var len = $("ul li").length;
var id = $("ul li").eq(len - 1).attr("id")
alert(id) // a5
// 对上面的出现的俩次 ul li 进行优化
var $li = $("ul li");
var len = $li.length;
var id = $li.eq(len - 1).attr("id")
alert(id) // a5
// 一个特殊的选择器 可阅读jQuery参考手册去进行理解
var id = $("ul li:last").attr("id")
alert(id) // a5
3、链式调用
核心:合理使用this关键字,以return this 的形式。
原话:刚才的代码中频繁出现对象在调用函数之后,又立刻调用其他函数或者属性的情况,你可知道这是怎么回事,
为什么能够这么写?
示例:
var myFunction = function () {
return {
func01: function () {
console.log("func01")
return this;
},
func02: function () {
console.log("func02")
return this;
}
}
}
console.log("正常调用:")
var obj = myFunction();
obj.func01()
obj.func02()
console.log("链式调用:")
obj.func01().func02()
缘由:让func01的返回值变成obj就行了。一个this关键字就可以解决这个问题,因为在函数中,this 关键字永远
指向当前函数的调用者。在这里,调用者自然是obj。
4、创造新的元素
主要就是使用append和appendTo新增节点,也可以理解为追加元素。
// 创建一个新的元素
var newLi = $("<li id='a6'>新的法宝</li>")
newLi.appendTo($('ul'))
// 或者 反斜杠 表示转义 只可以使用appendTo
var newLi1 = $("<li id="a7">新的法宝1</li>")
newLi1.appendTo($('ul'))
// 一气呵成
$("<li id='a8'>新的法宝2</li>").appendTo($('ul'))
// append方法
$('ul').append($("<li id='a9'>新的法宝3</li>"))
// 替换目标元素里面的html代码 这样的话,整个法宝列表只有这一个元素了
$('ul').html($("<li id='a5'>法宝被替换了</li>"))
jQuery插入节点的其他方法:
// 示例
$("<li id='a9'>新的法4</li>").insertBefore($('ul')) // 在ul 的最上面 外部 加了一个 li
$("<li id='a10'>新的法5</li>").insertAfter($('ul')) // 在ul 的最下面 外部 加了一个 li
// 将 新的法宝6 添加到该ul 的最前面
$("<li id='a10'>新的法宝6</li>").prependTo($('ul'))
5、删除和隐藏节点
使用 remove 删除节点
使用 hide 隐藏节点
// 删除id为a1的元素
$("#a1").remove();
$("#a2").hide(); // 隐藏 其实就是给对应的元素设置display:none。
$("#a2").show(); // 显示 show方法其实就是把元素的display重新设置为block罢了
6、jQuery操作属性
使用attr方法
// 给元素设置多个属性
// 需要给attr方法传入一个JavaScript对象,对象里面是键值对的集合,
// 每个键值对的格式为key:value,不同的键值对用逗号分隔
$("#a31").attr({"name":"spanDom","title":"赋值标题"})
// 删除属性 传入想要删除的属性名
$("#a31").removeAttr('name')
7、内容操作
如何用jQuery设置和获取HTML、文本和值?
text方法用来设置和获取元素的文本内容。 -- 无参的时候 获取内容 有参数的时候 替换内容
val方法操作元素的值,传入参数就是给元素赋值,不传参数的话,就是获取元素的值。
html方法的含义是把HTML代码的字符串动态地插入目标元素内部
<div id="list"></div>
$("#list").html("<span style='color:red'>新文本内容</span>")
console.log($("#list").text()) // 新文本内容
console.log($("#list").html()) // <span style="color:red">新文本内容</span>
console.log($("h3").html()) // 法宝列表
8、遍历和寻找节点
children方法:获取某个元素的下一代子元素,但不包括孙子辈的元素。
find方法:去寻找指定标签下面的元素
siblings方法:获取同一级别的所有兄弟元素
四、vue的妙处
1、数据绑定
代码语言:javascript复制使用vue的时候先引入:
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
使用v-model 完成数据的双向绑定
示例:
<div id="app">
<input type="text" v-model="message"> <br>
输入的文字:{{message}}
</div>
<script>
new Vue({ // 创建vue对象,类似于java的spring框架
el:'#app',
data:{
message:''
}
})
</script>
Vue() 可以理解为一个构造函数,里面传一个参数 也就是json对象,你看:Vue({})
2、v-on 监听事件
代码语言:javascript复制v-on 监听事件 可以简化为 @ 效果是一样的
使用场合:鼠标单击、键盘事件、下拉框等
示例:
<input type="button" @click="add" value="点击一下就加1">
<input type="button" v-on:click="sub" value="点击一下就减1">
<p>{{num}}</p>
<script>
new Vue({
el:'#app',
data:{
num:1
},
methods:{
add(){
this.num
},
sub(){
this.num--
}
}
})
</script>
处理事件冒泡:有如下几种
.stop 阻止事件冒泡: @click.stop="btn"
.prevent 阻止提交,只对from标签和a标签有效 阻止这种默认的刷新操作
.capture 优先触发
.self 只允许元素自己触发,子元素无法触发
.once 只触发一次
3、条件语句
代码语言:javascript复制v-if
v-else
v-else-if
示例:仅为了演示
<p v-if="flag">张三</p>
<p v-else="flag">李四</p>
数据域里面:改变false,看效果,理解
data:{
flag:true
}
4、for循环
代码语言:javascript复制示例:
<tr>
<td>请选择身份:</td>
<td>
<select>
<option v-for="role in roleList" value="role.value">{{role.label}}</option>
</select>
</td>
</tr>
data:{
roleList:[
{value:'1',label:'我是游客'},
{value:'2',label:'我是普通用户'},
{value:'3',label:'我是管理员'}
]
}
5、属性绑定 v-bind 简写 :
代码语言:javascript复制 要加载绑定属性的前面
示例:给optiona绑定value属性
指定默认选中:我是普通用户
<tr>
<td>请选择身份:</td>
<td>
<select v-model="role">
<option v-for="role in roleList" :value="role.value">{{role.label}}</option>
</select>
</td>
</tr>
data:{
role:2,
roleList:[
{value:'1',label:'我是游客'},
{value:'2',label:'我是普通用户'},
{value:'3',label:'我是管理员'}
]
}
6、组件开发
代码语言:javascript复制 在components 下定义局部组件:定义组件的时候有大写字母,使用的时候写横杠和小写字母
示例1:定义一个按钮组件,基本使用
<cool-btn name="按钮1"></cool-btn>
<cool-btn name="按钮2"></cool-btn>
// 局部组件
components:{
'coolBtn':{
props:['name'], // 添加组件的属性
template:"<input :value='name' type='button' style='background: pink'/>"
}
}
示例2:显示不同风格的按钮
<style>
.primary{background:#409eff}
.danger{background: #f56c6c}
.success{background: #67c23a}
.warning{background: #e6a23c}
</style>
<cool-btn name="按钮1" type="primary"></cool-btn>
<cool-btn name="按钮2" type="success"></cool-btn>
components:{
'coolBtn':{
props:['name','type'], // 添加组件的属性
template:"<input :value='name' :class='type' type='button' />"
}
}
全局组件:所有页面都可以使用的,最后,在外面建议一个btnUtil.js(名字是任意的) 将代码复制进去
使用的时候,在引入该组件
格式:Vue.component('组件名',{props:['属性名'],template:"模板信息"});
7、计算属性:computed 与 data、methods等是平级的
代码语言:javascript复制情景导入:商品打折案例
请输入商品价格:
<input type="number" v-model="price"><br>
请输入商品折扣:
<select v-model="discount" style="width: 100px">
<option v-for="item,index in discounts" :value="item.value">{{item.label}}</option>
</select><br>
<span style="color: #F00">成交价:{{price*discount}}元</span>
new Vue({
el:'#app',
data:{
price:0,
discount:0.9,
discounts:[
{value:'0.9',label:'9折'},
{value:'0.8',label:'8折'},
{value:'0.7',label:'7折'},
{value:'0.65',label:'65折'}
]
},
methods:{
},
computed:{
}
})
当计算过程非常复杂怎么办?
现在要求判断日期,如果当天是11月11号,那么就需要搞促销,规定商 品满200元减50元,满400元减150元
自己可以用今天的日期记性验证一下。
计算属性:
computed:{
payment(){
// 1、获取今天的日期
var today = new Date();
var month = today.getMonth() 1;
var day = today.getDate();
// 2、根据是否是双十一,获取返利的金额
var rebate = 0;
if(month == 11 && day == 11){
if(this.price>=400){
rebate = 150;
}else if(this.price>=200){
rebate = 50;
}
}
// 3、得到最终的金额
return this.price * this.discount - rebate;
}
}
页面的返回值要改一下:
<span style="color: #F00">成交价:{{payment}}元</span>
8、监听属性:和data、methods平级的
代码语言:javascript复制Vue可以监听属性的变化,属性既可以是data里面定义的数据变量,也可以是自己的props模块中定义的数据。
示例:有一个进度条,它的旁边有一个“增加进度”按钮,当进度达到不同的百分比时,就在进度条上方显示不同的提示
<style>
.progress{
display: inline-block;
background: red;
width: 0px;
height: 18px;
}
</style>
<div id="app">
<h2>{{msg}}({{progressNum}}%)</h2>
<span class="progress" :style="spanStyle"></span><br>
<input type="button" value="增加进度" @click="add">
</div>
new Vue({
el:'#app',
data:{
progressNum:0,
msg:"加油,看好你!"
},
methods:{
add(){
this.progressNum =10
}
},
watch:{
progressNum(val){
if(this.progressNum>=100){
this.msg='大功告成,辛苦了!'
return;
}
var background = 'red';
if(this.progressNum>=80){
background = 'green'
this.msg='快了,就差一点了!'
}else if(this.progressNum>=50){
background = 'green'
this.msg='有待改善!'
}else if(this.progressNum>=30){
background = 'pink'
this.msg='有点进展了!'
}
this.spanStyle={
width:this.progressNum 'px',
background:background
}
}
}
})
把对进度条的操作都转移到了监听函数里面,保证了 addProgress函数的干净整洁,大幅提升了程序代码的可读性
9、过滤器
代码语言:javascript复制哪个数据需要这个过滤器进行过滤,就在竖线后面加上该过滤器。
局部过滤器:
示例:只显示年月日
<p>今天是:{{today|dateFormat}}</p>
data:{
today:new Date()
},
filters:{
dateFormat(val){
return val.getFullYear() '-' (val.getMonth() 1) '-' val.getDate()
}
}
过滤器还可以叠加使用,比如给日期添加多个过滤器 可以嵌套使用
示例:添加样式过滤器
<p>今天是:<span v-html="$options.filters.dateFormat(today)"></span></p>
<p>今天是:<span v-html="$options.filters.boxStyle($options.filters.dateFormat(today))">
</span></p>
filters:{
dateFormat(val){
return val.getFullYear() '-' (val.getMonth() 1) '-' val.getDate()
},
boxStyle(val){
return "<span style='background: pink' >" val "</span>";
}
}
全局过滤器:接受俩个参数,第一个是过滤器的名字,第二个是具体实现函数
Vue.filter('dateFormat',function (val) {
return val.getFullYear() '-' (val.getMonth() 1) '-' val.getDate()
})
五、vue-cli项目
1、node.js安装
代码语言:javascript复制直接去官网下载,安装即可
官网地址:http://nodejs.cn
想看下是否安装成功,可以在cmd命令窗口下输入:node -v 会出现node的版本信息
2、npm发布模块
代码语言:javascript复制先到npm.js注册一个账户,才有权限发布模块
3、npm安装模块
代码语言:javascript复制格式:
npm install 模块名
4、搭建vue-cli项目
代码语言:javascript复制vue-cli就是vue脚手架,可以理解为一个项目模板。
安装vue-cli
npm install -g vue-cli
创建项目
vue init webpack 项目名
六、ES6语法
1、全新的变量定义
代码语言:javascript复制使用const 定义常量,定义了之后,不能再修改。
使用let 定义局部变量,例如let定义在for循环中的变量。
2、变量的解构赋值
代码语言:javascript复制变量的解构赋值分为:
数组的解构赋值
对象的解构赋值
用的较多的是变量的解构赋值
可用于新增和修改
常规做法:
// 一个Person对象定义了俩个方法
let Person={
eat:function () {
console.log("我在吃饭!")
},
sleep:function () {
console.log("我在睡觉")
}
}
// 常规做法
let eat = Person.eat;
let sleep = Person.sleep;
eat() // 我在吃饭!
sleep() // 我在睡觉
// 解构赋值
let Person={
eat:function () {
console.log("我在吃饭!")
},
sleep:function () {
console.log("我在睡觉")
}
}
// 解构赋值的作用:把获取对象中的方法以及赋值给对应变量的过程一次性做完
let {eat,sleep} = Person;
eat()
// 新增一个name属性
let {eat,sleep,name} = Person;
eat()
console.log(name); // undefined
// 给name属性赋默认值
let {eat,sleep,name="一个神秘的杠精"} = Person;
eat()
console.log(name); // 一个神秘的杠精
小结:在实际项目中,解构赋值可以带来方便,同时解构赋值还有很多其他高级用法,但是笔者不推荐使用过多解构
赋值,因为这会导致代码过于精简和抽象,不利于后期的维护。如果 后面的项目被一个初来乍到的新人接管,则会给
他带来很多困扰。
案例:用于修改。data里面包含所有需要修改的信息,只需要让页面加载这些信息就行
function initEditInfo(data = {name:'jack',sex:'1',salary:'20000'}) {
let{name,sex,salary} = data;
console.log(name)
console.log(sex)
console.log(salary)
}
initEditInfo()
initEditInfo({name:"张三"})
直接调用了初始化方法initEditInfo,什么参数都没有传递,但是因为在参数这里使用了默认值,于是就采用
{name:'jack',sex:'1', salary:'20000'}这个默认对象了 。
3、字符串升级
代码语言:javascript复制字符串允许被当作数组一样使用。换句话 说,你可以用下标的方式获取字符串中某个位置的字符
1、允许字符串直接通过for循环的方式遍历
示例:
let str = "北京天安门"
// 使用of遍历字符串
for (let s of str){
console.log(s)
}
// 使用in遍历字符串 in 获取的是每个字符对应的下标
for (let s in str){
console.log(s "---->" str[s])
}
// 0---->北
// 1---->京
// 2---->天
// 3---->安
// 4---->门
2、允许用反引号进行一些简化的字符串定义。模板字符串相当于加强版的字符串,它除了可以作为普通字符串使用,
还可以用来定义多行字符串,以及在字符串中加入变量和表达式。
示例:
let name = "张三"
let sayHi = `你好啊,${name}`
console.log(sayHi) // 你好啊,张三
ES6字符串还提供了一些非常好用的API 方法,如字符串补全。
假设现在有一个需求是依次打印0~99,但是不足2位的数字需要用0左补齐,以往的做法是用if进行判断,
如果小于10, 就在左边加一个0。
示例:
// padStart:返回新的字符串,表示用参数字符串从头部(左侧)补 全原字符串。
// padEnd:返回新的字符串,表示用参数字符串从尾部(右侧)补全 原字符串。
for (let i = 0;i < 100; i ){
console.log(i.toString().padStart(2,'0'))
}
以上两个方法可以接收两个参数,第一个参数是指定生成的字符串的最小长度,第二个参数是用来补全的字符串。
如果没有指定第二个数,则默认用空格填充。
4、Proxy代理
代码语言:javascript复制 在支持Proxy的浏览器环境中,Proxy是一个全局对象,它可以被直 接使用。Proxy(target,handler)是
一个构造函数,target是被代理的对 象,handlder是声明了各类代理操作的对象,最终返回一个代理对象。 外界
每次通过代理对象访问target对象的属性时,就会经过handler对象,从这个流程来看,代理对象很类似
middleware(中间件)。那么, Proxy可以拦截什么操作呢?最常见的就是get(读取)、set(修改)对象属性等
操作。
简单来说,Proxy的作用就是允许我们声明一个代理,对某个对象进行一些特殊的访问拦截。一个Proxy对
象由两个部分组成:target、 handler。在通过Proxy构造函数生成实例对象时,需要提供这两个参数。target
即目标对象,handler是一个对象,声明了代理target的指定行为。
示例:
let obj = {
name:'keke',
age: 28
}
console.log(obj.age) // 28
obj.age="18"
console.log(obj.age) // 现在是可以修改成功的 18
// 希望只给age属性赋值的类型为number 给对象赋值的时候限制一下类型
let obj = {
name:'keke',
age: 28
}
let objProxy = new Proxy(obj,{
set(target,key,value){
if(key == 'age' && typeof value != 'number'){
throw new Error(`你赋值的age不是number,Invalid attempt to set ${key} to
"${value}" : not number!`);
}
return target[key] = value;
},
get(target,key,receiver){
return target[key];
}
})
objProxy.age="18"
console.log(obj.age) // 你赋值的age不是number,Invalid attempt to set age to "18" :
// not number!
5、强化后的数组
代码语言:javascript复制1、快速构建新数组
Array.of方法可以将参数中的所有值作为元素而形成数组,参数值可以是不同类型。
如果参数为空时,则返回空数组。这一点很好理解。
需要重点介绍的是Array.from方法,这个方法可以将类数组对象或可迭代对象转化为数组。
类数组对象就是一种可以遍历的对象,只要对象有 length属性,而且有诸如0、1、2、3这样的属性,
那么它就可以被称为类数组。
let listData = {
0:'keke',
1:'jerry',
length:2
}
console.log(listData) // 对象 0: "keke",1: "jerry",length: 2
// 但是它毕竟不是数组,不方便进行某些操作,如push。我们可以用 from方法将它转换为数组。
listData = Array.from(listData)
console.log(listData) // ['keke', 'jerry']
// from方法还 可以接收第二个参数,就是一个map回调函数,用于对每个元素进行处理,放入数组的是处理后的元素
listData = Array.from(listData,function (item){
return item '--';
} )
console.log(listData)
2、新的数组方法
find:查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素。
findIndex:查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。
fill:将一定范围索引的数组元素内容填充为单个指定的值。
copyWithin:将一定范围索引的数组元素修改为此数组另一指定范围索引的元素。
entries:遍历。 keys:遍历键名。 values:遍历键值。
3、数组复制
以前大多采用:slice方法
现在可以使用 ... 的方式快速复制一个数组
示例:
let newData = [...listData]
console.log(newData)
6、箭头函数
代码语言:javascript复制箭头函数:省略function关键字,使用箭头声明函数
如果函数体仅仅是一个简单的return语句,那么函数体的大括号和return关键字都可以省略。
关于箭头函数中的this用法,还得多琢磨琢磨!
箭头函数中this的指向是定 义时所在的作用域,而不是执行时的作用域
只要使用了箭头函数,就不要管这个函数本身了,在外面寻找最近的大括号,然后看这个大括号处在怎样的环境中,
this就指向它!
使用示例:
let func1 = () => 19;
console.log(func1()) // 19
let func2 = (num) => num;
console.log(func2(18)) // 18
let sum = (num1,num2) => num1 num2;
console.log(sum(1,2)) // 3
// 用下划线 代替小括号 相当于有一个参数 这个参数不可以使用
let fun3 = _ =>{
console.log("大帝!")
}
fun3()
7、灵活多变的对象
代码语言:javascript复制 示例:
let name = '杠精'
let obj = {
name:name
}
console.log(obj) // {name: '杠精'}
// 只要左右俩边的参数是一样的 就可以简写
let obj2 = {name}
console.log(obj2) // {name: '杠精'}
let key = 'name'
let obj3={
[key]:'神秘的杠精' // 可以用 . 也可以用[]
}
console.log(obj3) // {name: '神秘的杠精'}
8、promise对象和async函数
代码语言:javascript复制示例:制作一个定时器,2s过后,获取一个字符串,然后在控制台输出这个字符串。
let gift = null;
setTimeout(_ =>{
gift = "功法"
},2000)
console.log('我收到了礼物:' gift) // 我收到了礼物:null
// 这是一个异步操作,需要在2s后才会执行 gift变量的赋值语句,所以,还没有等gift有值,语句就已经输出了
promise是异步编程的一种解决方案。从语法上说,promise是一个 对象,使用它可以获取异步操作的消息。
示例改造:2秒后输出到控制台
let gift = null;
new Promise((resolve, reject) => {
setTimeout(_=>{
gift = "笔记本"
resolve(gift)
},2000);
}).then(gift => {
console.log("我收到了礼物:" gift)
})
promise对象在创建的时候分别接收了2个内部的函数钩子:
resolve(已完成)和reject(已拒绝)。
promise对象就是一种承诺,在必要的时候,它会告知外部本次异步操作已经完成或者拒绝,
如果是完成,则触发后面的then方法;
如果是拒绝,则触发catch方法。
示例再次改造:有20%的概率可以获得礼物,有80%的概率不能获得礼 物,即表示获取异常(reject)
let gift = null;
new Promise((resolve, reject) => {
setTimeout(_=>{
if(Math.random()<0.2){
gift = "笔记本"
resolve(gift)
}else{
gift = "空空如也"
resolve(gift)
}
},2000);
}).then(gift => {
console.log("我收到了礼物:" gift)
}).catch(gift =>{
console.log("我收到了礼物:" gift)
})
示例再次改造:脱离then和catch的回调函数
let getGiftAsync = _=>{
return new Promise((resolve, reject) => {
setTimeout( _=>{
if(Math.random()<0.5){
let gift = "花果茶"
resolve(gift)
}else{
let gift = "空空如也"
resolve(gift)
}
},2000);
})
}
async function executeAsyncFunc() {
let gift = await getGiftAsync();
console.log(gift)
}
executeAsyncFunc();
getGiftAsync函数返回了一个promise对象,逻辑和刚才一样,然后在executeAsyncFunc函数的左边加上
了async,代表这是一个异步处理函数。只有加上了async关键字的函数,内部才可以使用await关键字。
async是ES7才提供的与异步操作有关的关键字,async函数执行时,如果遇到await就会先暂停执行,等到触发
的异步操作完成后,才会恢复 async函数的执行并返回解析值。