JavaScript学习

JavaScript学习

最近在学习前端,js肯定是绕不过的一道坎,所以就写篇博客记录一下吧,供自己之后复习查看,当然如果能帮到你那就更好了,整个学习过程是按照廖雪峰的js教程进行的。

快速入门部分已经在我的另一篇博客中了,这里就直接跳过了那一部分。

1.函数

1.1 函数的定义和调用

函数和其他语言都比较相似,使用function关键字定义:

function abs(x){
    return x>0?x:-x;
}
console.log(abs(-1));

wAOKjH.png

由于Javascript中函数也是一个对象,所以就有如下的等价写法:

var abs = function(x){
    return x>0?x:-x;
};
console.log(abs(-1));

关于函数的参数说明

  1. JavaScript对传入参数不限制,多于或者小于指定参数都是OK的。
function abs(x){
    return x>0?x:-x;
};
console.log(abs(-1,'abc','xyz'));

wAOsU0.png

  1. Javascript提供了一个arguments关键字,他指向传入的所有参数,类似于一个数组。
function length(){
    for(var x of arguments){
        console.log(x)
    }
    return arguments.length
};
console.log(length(-1,'abc','xyz'));
console.log(length());

wAXQRU.png

  1. Javascript还提供了一个rest关键字,用于接收多余的参数,通过下面的代码就能明白了~
function greater(a, b, ...rest){
    console.log(a);
    console.log(b);
    console.log(rest)
};
console.log(greater(1,2,3,4,5));

wAjMTI.png

1.2 变量作用域

不在任何函数内使用var定义的变量就具有全局作用域,而在函数中定义,就只能在函数体中使用。(let就只能在自己所在的块级作用域中使用,比如for循环,if判断等到)

function test(){
    var x = 1;
    x = x + 1;
}
console.log(x)

wAxoOe.png

JS的函数可以嵌套,内部函数可以调用外部函数的变量

function test1(){
    var x = 1;
    function test2(){
        console.log(x)
    }
    test2()
}
test1()

名字空间

全局变量会绑定到全局对象window上,每一个变量就是全局对象window的一个属性

var x = 1;
function test(){
    console.log('I have been called!')
}
console.log(window.x)
window.test()

wAzUne.png

实际中可能会有多个JS文件,因此全局对象中的变量名就容易产生冲突,一种解决的方法就是将所有变量和函数都绑定到一个全局变量中。

var MYAPP = {}
MYAPP.x = 1;
MYAPP.test = function(){
    console.log('I have been called!')
}
console.log(window.MYAPP.x)
window.MYAPP.test()

wAzUne.png

解构赋值

非常方便的一个功能,能够对数组,对象等结构进行解构赋值,只需要保证对应的结构层次相同即可。

var [x,[y, ]] = [1,[2,3]]   // 忽略了第三个元素
console.log(x,y)
var person = {
    name : 'Alice',
    age : 18,
    family : {
        father : 'Bob',
        mother : 'Anna'
    }
};
var {name,age,family:{father,d}} = person;  // 由于d与属性名不同,因此无法解构
console.log(name,age,father,d)
var m = 1, n = 2;
[m, n] = [n, m]
console.log(m,n)    // 交换两个数

wEp4S0.png

1.3 方法

一个对象中绑定函数,称为这个对象的方法,我们经常需要使用到一个关键字this,它始终指向当前对象(this有很多坑,这里就不细说了)

var xiaoming = {
    name : '小明',
    birth : 1990,
    age : function(){
        var y = new Date().getFullYear();
        return y - this.birth;
    }
};
console.log(xiaoming.age())

wEPsOK.png

当然也可以将方法写在外面:

function computeAge(){
    var y = new Date().getFullYear();
    return y - this.birth;
}
var xiaoming = {
    name : '小明',
    birth : 1990,
    age : computeAge
};
console.log(xiaoming.age())

1.4 高阶函数

当一个函数接收另一个函数作为参数时,这个函数就称之高阶函数,举个小栗子:

function add(x, y, f){
    return f(x)+f(y);
}
var x = add(-4, 5, Math.abs)
console.log(x)

wEFRxI.png

1.4.1 map/reduce

Javascript的Array中定义了map()方法,用于构建一种映射关系,直接看代码就明白了~

var arr = [1,2,3,4,5];
function pow(x){
    return x*x;
}
console.log(arr.map(pow));

wEk1SA.png

同样还有reduce方法,执行效果如下:

[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)

所以要求我门传入的函数接收参数为两个,举个栗子:

var arr = [1,2,3,4,5];
function add(x, y){
    return x + y;
}
console.log(arr.reduce(add));

wEkryq.png

1.4.2 filter

从名字就能知道,这是一个过滤器,他和map类似,传入一个函数,然后作用于数组的每一个元素,不同的是,传入的函数返回值要求为一个bool类型,根据是否为true来决定该元素是否保留。

var arr = [1,2,3,4,5];
var afterFilter = arr.filter(function(x){
    return x%2 == 0;    // 过滤奇数
})
console.log(afterFilter);

wEAs9H.png

filter可以接收多个参数,上述我们只使用了第一个参数(即元素本身),当然还有其他参数:

var arr = ['a','b','c','d','e'];
var afterFilter = arr.filter(function(element, index, self){
    console.log(element);   // 元素本身
    console.log(index);     // 元素对应下标
    console.log(self);      // 数组本身
    return true;
})

wEEodK.png

巧妙利用这三个参数就能实现很多想法,比如元素去重~

var arr = ['a','a','a','b','c'];
var afterFilter = arr.filter(function(element, index, self){
    return self.indexOf(element) == index;
})
console.log(afterFilter)

wEVPJg.png

1.4.3 sort

排序在程序中是经常使用到的,但是如果直接使用Javascript的排序,很有可能就会出错(太狗血了~)

// 看上去正常的结果:
['Google', 'Apple', 'Microsoft'].sort(); // ['Apple', 'Google', 'Microsoft'];

// apple排在了最后:
['Google', 'apple', 'Microsoft'].sort(); // ['Google', 'Microsoft", 'apple']

// 无法理解的结果:
[10, 20, 1, 2].sort(); // [1, 10, 2, 20]

造成这个结果就是因为JS默认是按照字典序排序的(ASCII码),就算是数字,也会将其先转换为字符串再进行排序,所以自定义我们的排序算法就很有必要了。

var arr = [5,4,3,2,1]
arr.sort(function(x,y){
    if(x < y){
        return -1;
    }
    else{
        return 1;
    }
})
console.log(arr)

wELrRS.png

1.4.4 其他

array还有其他的很多使用的函数~

every() 可以用于判断数组的所有元素是否满足指定的测试条件:

var arr = ['apple', 'pear', 'orange']
console.log(arr.every(function(x){
    return x.length > 3;
}))

wEXkX4.png

find()/ findIndex() 用于查找符合条件的第一个元素,找到则返回这个元素(findIndex返回这个元素的坐标),未找到则返回undefined

var arr = ['apple', 'pear', 'orange']
console.log(arr.find(function(x){
    return x.length < 5;
}))
console.log(arr.findIndex(function(x){
    return x.length < 5;
}))

wEj6MD.png

forEach() 用于遍历数组,类似于for循环,依次将每个元素作用于传入的函数。

var arr = ['apple', 'pear', 'orange']
arr.forEach(function(x){
    var s = 'I like ';
    console.log(s+x);
})

wEvNSf.png

1.5 箭头函数

正如它名称所示,就是用箭头定义的函数~

比如这样:

var fn1 = x => x*x;     // 箭头函数
console.log(fn1(3));

var fn2 = function(x){  // 和上述写法等价
    return x*x;
}
console.log(fn2(3));

wVSjnU.png

箭头函数也可以包含多条语句,但是此时就需要添加{....}return了,多个参数用(...)括起来就好了~

var fn = (x,y,...rest) =>{
    var sum = x+y;
    for(var temp of rest){
        sum += temp;
    }
    return sum;
}
console.log(fn(1,2,3,4,5))

当然箭头函数和常规定义的函数还有有区别的,这里就没有进一步讨论了~

1.6 generator

generator(生成器)和函数非常相似,使用function*定义,它像是一个能返回多次值的函数,并且能够保存之前执行的状态(如果要使用函数的话可能就需要面向对象,使用对象的属性保存状态),直接看下面一个斐波拉契数列把~

function* fib(maxNum){
    var n = 0, a = 1, b = 1;
    while(n < maxNum){
        yield a;
        [a, b] = [b, a+b];
        n++;
    }
    return;
}
for(var x of fib(10)){
    console.log(x);
}

wVPQ3R.png

2. 标准对象

JS有许多的数据类型,如下:

console.log(typeof 123); // 'number'
console.log(typeof NaN); // 'number'
console.log(typeof 'str'); // 'string'
console.log(typeof true); // 'boolean'
console.log(typeof undefined); // 'undefined'
console.log(typeof Math.abs); // 'function'
console.log(typeof null); // 'object'
console.log(typeof []); // 'object'
console.log(typeof {}); // 'object'

wmlnh9.png

可以看到number,string,boolean,function,undefined都不是对象,但是其实number,string,boolean都是有包装对象的。

console.log(typeof new Number(12));
console.log(typeof new Boolean(true));
console.log(typeof new String('Hello'))

wmlmtJ.png

包装对象的值和原来的值相等,但是他已经变成一个对象了~

var num1 = new Number(12);
var num2 = 12;
console.log(num1 == num2);
console.log(num1 === num2);

wmlrB8.png

总结一下有以下几条规则需要遵守(这里直接采用廖雪峰的教程)

  • 不要使用new Number()new Boolean()new String()创建包装对象;
  • parseInt()parseFloat()来转换任意类型到number
  • String()来转换任意类型到string,或者直接调用某个对象的toString()方法;
  • 通常不必把任意类型转换为boolean再判断,因为可以直接写if (myVar) {...}
  • typeof操作符可以判断出numberbooleanstringfunctionundefined
  • 判断Array要使用Array.isArray(arr)
  • 判断null请使用myVar === null
  • 判断某个全局变量是否存在用typeof window.myVar === 'undefined'
  • 函数内部判断某个变量是否存在用typeof myVar === 'undefined'

1.1 Date

我们可以使用Date对象表示时间,直接上代码~

var now = new Date();   // 获取此时操作系统的时间
console.log(now);
console.log(now.toUTCString());     // 转化为格林威治时间
console.log(now.getFullYear());
console.log(now.getMonth());
console.log(now.getDate());
console.log(now.getDay());
console.log(now.getHours());
console.log(now.getMinutes());
console.log(now.getSeconds());
console.log(now.getMilliseconds());     // 获取毫秒
console.log(now.getTime());             // 用数字的形式表示的时间戳
var past = new Date(2000, 10, 25, 12, 0, 30);       // 自定义时间
console.log(past);

wm3GJH.png

可以看到Date使用起来十分方便,但是有非常坑的一个点就是JS的月份数字是从0开始的,所以才会有我们上述的10,25显示Nov 25的情况了。

1.2 RegExp

1.3 JSON

3. 面向对象编程

JS面向对象和其他语言有所不同,JS的类都是实例,想基于现有的类创建新的实例其实就是对原实例进行继承或者复制一个原实例再进行修改

// way 1
var student = {
    name:'Nothing',
    height:1.2,
    run: function(){
        console.log(this.name + ' is running...');
    }
};
var xiaoming = {
    name : 'xiaoming'
};
xiaoming.__proto__ = student;   // 从student上直接继承过来?
xiaoming.run()

wmBrkR.png

但是我们一般不用obj.__proto__去改变一个对象的原型(当我们在使用obj.xxx访问一个对象的属性时,优先是在当前对象上查找该属性,如果没有找到,就到其原型对象上找,找到即返回,否则一直上溯到Object.prototyoe对象,这样就构成了一个原型链,原型链很长就会花费更多时间查找!),通常使用Object.create()

// way 2
var student = {
    name:'Nothing',
    height:1.2,
    run: function(){
        console.log(this.name + ' is running...');
    }
};
function createStudent(name){
    var s = Object.create(student);     // 创建一个一摸一样的对象
    s.name = name;      // 修改属性
    return s;
}
 var xiaoming = createStudent('xiaoming');
 xiaoming.run();

输出结果同上

3.1 构造函数

我们也可以使用构造函数创建对象

function student(name){ // 默认return this,所以就不需要写了~
    this.name = name;
    this.run = function(){
        console.log(this.name + ' is running.');
    }
}
var xiaoming = new student('xiaoming');
xiaoming.run()

wmytUJ.png

3.2 对象继承

我们先实现第一步,基于现有的类构造新的类

function student(name){
    this.name = name || 'unamed';
}
function PrimaryStudent(name, grade){
    student.call(this, name);   // 基于student构造
    this.grade = grade;
}
var xiaoming = new PrimaryStudent('xiaoming', grade = 99);
console.log(xiaoming.name);
console.log(xiaoming.grade);

但是,调用了student构造函数并不等于继承了student,以下是它的原型链:

new PrimaryStudent() ----> PrimaryStudent.prototype ----> Object.prototype ----> null

而我们希望得到的原型链应该是这样子的(这样就能调用其父类所定义的方法~):

new PrimaryStudent() ----> PrimaryStudent.prototype ----> Student.prototype ----> Object.prototype ----> null

继承的方法感觉应该用不到,这里就跳过了~

3.3 class继承

在ES6中加入了新的关键字class,我们这里就用这个关键字来构造类:

class student{
    constructor(name){
        this.name = name;
    }
    hello(){    // 注意没有function字段
        console.log('Hello, '+ this.name+' !');
    }
}
var xiaoming = new student('xiaoming');
xiaoming.hello()

我们使用class可以很方便的继承对象,上代码

class student{
    constructor(name){
        this.name = name;
    }
    hello(){    // 注意没有function字段
        console.log('Hello, '+ this.name+' !');
    }
}
class primaryStudent extends student{
    constructor(name, grade){
        super(name);    // 调用父类构造函数
        this.grade = grade;
    }
    myGrade(){
        console.log('My grade is ' + this.grade + ' !');
    }
}
var xiaoming = new primaryStudent('xiaoming', 99);
xiaoming.hello()
xiaoming.myGrade()

wnDWSx.png

这样继承就非常的方便,所以我们可以不需要使用上述的原型继承(并且其实class和我们手动继承没有什么区别),只是可能有的浏览器不支持class,我们可以用Babel转化一下。

4. 浏览器

4.1 浏览器对象

4.1.1 window

window对象不但充当全局作用域,而且表示浏览器窗口

console.log(window.innerWidth);     // 显示网页的净宽高
console.log(window.innerHeight);
console.log(window.outerWidth);     // 整个浏览器窗口的宽高
console.log(window.outerHeight);
4.1.2 navigator

navigator对象表示浏览器的信息

console.log(navigator.appName);         // 浏览器名称(返回Netscape或者浏览器全名)
console.log(navigator.appVersion);      // 浏览器版本
console.log(navigator.language);        // 语言
console.log(navigator.platform);        // 操作系统类型
console.log(navigator.userAgent);       // 浏览器设定的userAgent字符串

wnc5oF.png

4.1.3 screen

screen对象表示屏幕的信息:

console.log(screen.width)       // 屏幕宽高
console.log(screen.height);
console.log(screen.colorDepth)  // 颜色位数

wngQln.png

4.1.4 location

location对象表示了当前页面的URL的信息:

console.log(location.href);     // 获取url
console.log(location.host);     // 以下依次获取url的各个部分
console.log(location.port);
console.log(location.pathname);
console.log(location.search);
console.log(location.hash);

wngf1A.png

4.1.5 document

document对象就是整个DOM树的根节点,这样就能获取网页的很多信息,甚至还能获取cookie,当然一些网站为了安全,会将cookie设置为httpOnly,这样将不能读取。

html页面:

<!DOCTYPE html>
<html>
    <head>
        <title>hello world</title>
    </head>
<body>
    <dl id = 'drink_menu'>
        <dt>摩卡</dt>
        <dd>热摩卡</dd>
        <dt>酸奶</dt>
        <dd>老酸奶</dd>
        <dt>果汁</dt>
        <dd>鲜榨苹果汁</dd>
    </dl>
    <script src = "main.js"></script>
</body>

</html>

JS代码:

console.log(document.title);    // 获取标题
document.title = 'I love JS';
console.log(document.title);    // 甚至修改标题

var menu = document.getElementById('drink_menu')    // 通过ID查找
console.log(menu)

var drinks = document.getElementsByTagName('dt')    // 通过标签名称查找
for(var x of drinks){
    console.log(x.innerHTML)
}

wnROLq.png

4.1.6 history

history对象保存了浏览器的历史记录,可以调用history对象的back()forward (),相当于用户点击了浏览器的“后退”或“前进”按钮。但是不推荐使用!

4.2 操作DOM

HTML文档被浏览器解析后就是一个DOM树,再操作DOM之前首先要获得这个DOM元素, 主要的方法是document.getElementById()document.getElementsByTagName()以及CSS选择器document.getElementsByClassName()(还可以使用querySelectorquerySelectorAll

直接看代码吧~

HTML代码

<!DOCTYPE html>
<html>
    <head>
        <title>hello world</title>
    </head>
<body>
    <div id = 'test_div'>
        <div class = 'c_red'>
            <p id = 'test_p'>Javascript</p>
            <p>JAVA</p>
        </div>
        <div class= 'c_red c_green'>
            <p>Python</p>
            <p>Ruby</p>
            <p>Switch</p>
        </div>
        <div>
            <p>Scheme</p>
            <p>Haskell</p>
        </div>
    </div>
    <script src = 'main.js'></script>
</body>

</html>

JS代码

var test1 = document.getElementById('test_div')
var test2 = document.getElementById('test_div').getElementsByClassName('c_red')
var test3 = test1.children
console.log(test1)
console.log(test2)
console.log(test3)

w3dvgs.png

4.2.1 更新DOM

更新DOM有两种方式:直接修改innerHTML属性或者innerText属性(或者textContentinnerText不返回隐藏元素),当然也可以修改CSS

HTML代码

<!DOCTYPE html>
<html>
    <head>
        <title>hello world</title>
    </head>
<body>
    <p id = 'p_id1'>abc</p>
    <p id = 'p_id2'>abc</p>
    <p id = 'p_id3'>abc</p>
    <p id = 'p_id4'>abc</p>
    <script src = 'main.js'></script>
</body>

</html>

JS代码

var p1 = document.getElementById('p_id1')       // 使用innerHTML
p1.innerHTML = 'ABC'
var p2 = document.getElementById('p_id2')       // 可以设置标签
p2.innerHTML = 'ABC<span style = \"color:red\">RED</span>'
var p3 = document.getElementById('p_id3')       // text无法设置标签
p3.innerText = 'Text'
var p5 = document.getElementById('p_id4')       // 设置CSS
p5.style.color = '#ff0000'
p5.style.fontSize = '20px'
p5.style.paddingTop = '2em'

w3WlIs.png

4.2.2 插入DOM
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值