JavaScript学习
最近在学习前端,js肯定是绕不过的一道坎,所以就写篇博客记录一下吧,供自己之后复习查看,当然如果能帮到你那就更好了,整个学习过程是按照廖雪峰的js教程进行的。
快速入门部分已经在我的另一篇博客中了,这里就直接跳过了那一部分。
1.函数
1.1 函数的定义和调用
函数和其他语言都比较相似,使用function
关键字定义:
1 | function abs(x){ |
由于Javascript中函数也是一个对象,所以就有如下的等价写法:
1 | var abs = function(x){ |
关于函数的参数说明
- JavaScript对传入参数不限制,多于或者小于指定参数都是OK的。
1 | function abs(x){ |
- Javascript提供了一个
arguments
关键字,他指向传入的所有参数,类似于一个数组。
1 | function length(){ |
- Javascript还提供了一个
rest
关键字,用于接收多余的参数,通过下面的代码就能明白了~
1 | function greater(a, b, ...rest){ |
1.2 变量作用域
不在任何函数内使用var
定义的变量就具有全局作用域,而在函数中定义,就只能在函数体中使用。(let
就只能在自己所在的块级作用域中使用,比如for循环,if判断等到)
1 | function test(){ |
JS的函数可以嵌套,内部函数可以调用外部函数的变量
1 | function test1(){ |
名字空间
全局变量会绑定到全局对象window
上,每一个变量就是全局对象window
的一个属性
1 | var x = 1; |
实际中可能会有多个JS文件,因此全局对象中的变量名就容易产生冲突,一种解决的方法就是将所有变量和函数都绑定到一个全局变量中。
1 | var MYAPP = {} |
解构赋值
非常方便的一个功能,能够对数组,对象等结构进行解构赋值,只需要保证对应的结构层次相同即可。
1 | var [x,[y, ]] = [1,[2,3]] // 忽略了第三个元素 |
1.3 方法
一个对象中绑定函数,称为这个对象的方法,我们经常需要使用到一个关键字this
,它始终指向当前对象(this有很多坑,这里就不细说了)
1 | var xiaoming = { |
当然也可以将方法写在外面:
1 | function computeAge(){ |
1.4 高阶函数
当一个函数接收另一个函数作为参数时,这个函数就称之高阶函数,举个小栗子:
1 | function add(x, y, f){ |
1.4.1 map/reduce
Javascript的Array
中定义了map()
方法,用于构建一种映射关系,直接看代码就明白了~
1 | var arr = [1,2,3,4,5]; |
同样还有reduce
方法,执行效果如下:
1 | [x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4) |
所以要求我门传入的函数接收参数为两个,举个栗子:
1 | var arr = [1,2,3,4,5]; |
1.4.2 filter
从名字就能知道,这是一个过滤器,他和map
类似,传入一个函数,然后作用于数组的每一个元素,不同的是,传入的函数返回值要求为一个bool
类型,根据是否为true
来决定该元素是否保留。
1 | var arr = [1,2,3,4,5]; |
filter可以接收多个参数,上述我们只使用了第一个参数(即元素本身),当然还有其他参数:
1 | var arr = ['a','b','c','d','e']; |
巧妙利用这三个参数就能实现很多想法,比如元素去重~
1 | var arr = ['a','a','a','b','c']; |
1.4.3 sort
排序在程序中是经常使用到的,但是如果直接使用Javascript的排序,很有可能就会出错(太狗血了~)
1 | // 看上去正常的结果: |
造成这个结果就是因为JS默认是按照字典序排序的(ASCII码),就算是数字,也会将其先转换为字符串再进行排序,所以自定义我们的排序算法就很有必要了。
1 | var arr = [5,4,3,2,1] |
1.4.4 其他
array
还有其他的很多使用的函数~
every() 可以用于判断数组的所有元素是否满足指定的测试条件:
1 | var arr = ['apple', 'pear', 'orange'] |
find()/ findIndex() 用于查找符合条件的第一个元素,找到则返回这个元素(findIndex返回这个元素的坐标),未找到则返回undefined
:
1 | var arr = ['apple', 'pear', 'orange'] |
forEach() 用于遍历数组,类似于for循环,依次将每个元素作用于传入的函数。
1 | var arr = ['apple', 'pear', 'orange'] |
1.5 箭头函数
正如它名称所示,就是用箭头定义的函数~
比如这样:
1 | var fn1 = x => x*x; // 箭头函数 |
箭头函数也可以包含多条语句,但是此时就需要添加{....}
和return
了,多个参数用(...)
括起来就好了~
1 | var fn = (x,y,...rest) =>{ |
当然箭头函数和常规定义的函数还有有区别的,这里就没有进一步讨论了~
1.6 generator
generator(生成器)和函数非常相似,使用function*
定义,它像是一个能返回多次值的函数,并且能够保存之前执行的状态(如果要使用函数的话可能就需要面向对象,使用对象的属性保存状态),直接看下面一个斐波拉契数列把~
1 | function* fib(maxNum){ |
2. 标准对象
JS有许多的数据类型,如下:
1 | console.log(typeof 123); // 'number' |
可以看到number
,string
,boolean
,function
,undefined
都不是对象,但是其实number
,string
,boolean
都是有包装对象的。
1 | console.log(typeof new Number(12)); |
包装对象的值和原来的值相等,但是他已经变成一个对象了~
1 | var num1 = new Number(12); |
总结一下有以下几条规则需要遵守(这里直接采用廖雪峰的教程)
- 不要使用
new Number()
、new Boolean()
、new String()
创建包装对象; - 用
parseInt()
或parseFloat()
来转换任意类型到number
; - 用
String()
来转换任意类型到string
,或者直接调用某个对象的toString()
方法; - 通常不必把任意类型转换为
boolean
再判断,因为可以直接写if (myVar) {...}
; typeof
操作符可以判断出number
、boolean
、string
、function
和undefined
;- 判断
Array
要使用Array.isArray(arr)
; - 判断
null
请使用myVar === null
; - 判断某个全局变量是否存在用
typeof window.myVar === 'undefined'
; - 函数内部判断某个变量是否存在用
typeof myVar === 'undefined'
。
1.1 Date
我们可以使用Date
对象表示时间,直接上代码~
1 | var now = new Date(); // 获取此时操作系统的时间 |
可以看到Date
使用起来十分方便,但是有非常坑的一个点就是JS的月份数字是从0
开始的,所以才会有我们上述的10,25
显示Nov 25
的情况了。
1.2 RegExp
1.3 JSON
3. 面向对象编程
JS面向对象和其他语言有所不同,JS的类都是实例,想基于现有的类创建新的实例其实就是对原实例进行继承或者复制一个原实例再进行修改。
1 | // way 1 |
但是我们一般不用obj.__proto__
去改变一个对象的原型(当我们在使用obj.xxx
访问一个对象的属性时,优先是在当前对象上查找该属性,如果没有找到,就到其原型对象上找,找到即返回,否则一直上溯到Object.prototyoe
对象,这样就构成了一个原型链,原型链很长就会花费更多时间查找!),通常使用Object.create()
。
1 | // way 2 |
输出结果同上
3.1 构造函数
我们也可以使用构造函数创建对象
1 | function student(name){ // 默认return this,所以就不需要写了~ |
3.2 对象继承
我们先实现第一步,基于现有的类构造新的类
1 | function student(name){ |
但是,调用了student
构造函数并不等于继承了student
,以下是它的原型链:
1 | new PrimaryStudent() ----> PrimaryStudent.prototype ----> Object.prototype ----> null |
而我们希望得到的原型链应该是这样子的(这样就能调用其父类所定义的方法~):
1 | new PrimaryStudent() ----> PrimaryStudent.prototype ----> Student.prototype ----> Object.prototype ----> null |
继承的方法感觉应该用不到,这里就跳过了~
3.3 class继承
在ES6中加入了新的关键字class
,我们这里就用这个关键字来构造类:
1 | class student{ |
我们使用class
可以很方便的继承对象,上代码
1 | class student{ |
这样继承就非常的方便,所以我们可以不需要使用上述的原型继承(并且其实class
和我们手动继承没有什么区别),只是可能有的浏览器不支持class
,我们可以用Babel转化一下。
4. 浏览器
4.1 浏览器对象
4.1.1 window
window
对象不但充当全局作用域,而且表示浏览器窗口
1 | console.log(window.innerWidth); // 显示网页的净宽高 |
4.1.2 navigator
navigator
对象表示浏览器的信息:
1 | console.log(navigator.appName); // 浏览器名称(返回Netscape或者浏览器全名) |
4.1.3 screen
screen
对象表示屏幕的信息:
1 | console.log(screen.width) // 屏幕宽高 |
4.1.4 location
location
对象表示了当前页面的URL的信息:
1 | console.log(location.href); // 获取url |
4.1.5 document
document
对象就是整个DOM树的根节点,这样就能获取网页的很多信息,甚至还能获取cookie,当然一些网站为了安全,会将cookie设置为httpOnly
,这样将不能读取。
html页面:
1 |
|
JS代码:
1 | console.log(document.title); // 获取标题 |
4.1.6 history
history
对象保存了浏览器的历史记录,可以调用history
对象的back()
或forward ()
,相当于用户点击了浏览器的“后退”或“前进”按钮。但是不推荐使用!
4.2 操作DOM
HTML文档被浏览器解析后就是一个DOM树,再操作DOM之前首先要获得这个DOM元素, 主要的方法是document.getElementById()
和document.getElementsByTagName()
以及CSS选择器document.getElementsByClassName()
(还可以使用querySelector
和querySelectorAll
)
直接看代码吧~
HTML代码
1 |
|
JS代码
1 | var test1 = document.getElementById('test_div') |
4.2.1 更新DOM
更新DOM有两种方式:直接修改innerHTML
属性或者innerText
属性(或者textContent
, innerText
不返回隐藏元素),当然也可以修改CSS
HTML代码
1 |
|
JS代码
1 | var p1 = document.getElementById('p_id1') // 使用innerHTML |