面向Android的快速学习JavaScript笔记

概述

这篇文章是我自己在学习廖雪峰大神的JavaScript教程关于JavaScript的总结,后续会补上全部的总结笔记。
主要用于我自己日后的快速回顾检索重点知识用的。
结合自身,这篇笔记对于有JavaSE基础的人来说,可以达到极速入门JavaScript的效果(毕竟java和JavaScript有很多相似的地方)
笔记对于重复的部分不做描述,重点放在JS的特性上,所以也命名为面向Android的JS学习
如有理解错误的地方,希望大家可以踊跃指出,我立即修改

@(JavaScript)


数据类型

  • number
    • 基本:整数,浮点数,科学计数法,负数
    • NaN : not a number,只能用isNaN()来判断
    • Infinity : 表示无限大,超过了Number所能表示的最大值
  • 字符串
  • 布尔值
  • null
  • undefined
    • Number String Null Undefined Boolean 是JS的基本数据类型
  • 数组
    • 直接赋值会自动填充如:arr[10],缺省的部分自动填充undefined
  • 比较运算符
    • 使用===代替==,==会自动转换类型,而===不会自动转换
    • 运算符顺序:算术运算符 > 比较运算符 > 逻辑运算符 > 赋值运算符
  • 对象
  • 变量
    • 动态编译,所以全部指定为var, 不指定就为全局变量

字符串

  • 换行:\n, 或者直接换行(在ES6才能直接使用)
  • 拼接: + 或者 var name = 'hello'; ${name}
  • 操作:
    • str.length
    • str[num] , 不能直接通过下标修改字符串
    • toUpperCase() 大写转换
    • toLowerCase() 小写转换
    • indexOf() 检索
    • substring() 截取

数组

  • 常规属性不做描述了,数组大小是动态改变
    -
var arr = [1, 'hello', 3];
arr.length; // 3
arr.length = 6;
arr; // arr变为[1, 'hello', 3, undefined, undefined, undefined]
arr[7] = 4;
arr; // arr变为[1, 'hello', 3, undefined, undefined, undefined, undefined, 4]
arr.length = 2;
arr; // arr变为[1, 'hello']
  • indexOf() 检索
  • slice() 等于 substring() 截取
  • push() 和 pop() 参考队列使用,入队和出队
  • unshift() 和 shift() 头部的 入队 和 出队
  • sort() 排序 ,number自动转换成String,String按照ASCII排序
  • reverse() 反转
  • splice() 万能方法:
    • splice()方法是修改Array的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:
var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
// 从索引2开始删除3个元素,然后再添加两个元素:
arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
// 只删除,不添加:
arr.splice(2, 2); // ['Google', 'Facebook']
arr; // ['Microsoft', 'Apple', 'Oracle']
// 只添加,不删除:
arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
  • concat() 拼接字符串
  • join() 使数组每个字符串用指定字符串连接
    • 如果Array的元素不是字符串,将自动转换为字符串后再连接。
var arr = ['A', 'B', 'C', 1, 2, 3];
arr.join('-'); // 'A-B-C-1-2-3'

对象

  • JavaScript的对象是一种无序的集合数据类型,它由若干键值对组成
    • 键值对: 属性:属性名
    • 属性使用:
 var xiaohong = {
    name: '小红',
    'middle-school': 'No.1 Middle School'
};
//属性使用
xiaohong['middle-school']; // 'No.1 Middle School'
xiaohong['name']; // '小红'
xiaohong.name; // '小红'
  • JavaScript对象是动态类型,所以可以动态添加属性
    • bean.age=18; // 这样属性就添加成功了
    • delete bean.age ; // 删除属性成功
  • in字符检测 对象是否包含属性
    • age in bean // true or false

条件判断

JS中的if...else...的使用和Java基本一样
- 如果不加{}使用的话, if...else...只能作用他下面的第一行

var age = 20;
if (age >= 18)
    alert('adult');
else
    console.log('age < 18'); // 添加一行日志
    alert('teenager'); // <- 这行语句已经不在else的控制范围了
  • 在if..else if...else...的多行条件判断下,前面满足条件就不会执行后面的情况了

循环

  • for循环
    -
var x = 0;
var i;
for (i=1; i<=100; i++) {
    x = x + i;
}
x; // 5050

1: i = 1; 初始条件,执行一次
2:i <=100 ; 判断条件 ,不满足就退出循环
3: x = x+i ; 具体循环的业务逻辑
4: x++;  每次循环的递增条件

!!所以循环的顺序是  
1 > 2 > 3 > 4 > 2 > 3 > 4  > 2 ...... > 2(不满足条件 ,退出循环)  
  • for...in (比较像Java的foreach)
    • 遍历的实际上是对象的属性名称。一个Array数组实际上也是一个对象,它的每个元素的索引被视为一个属性。
    • 对 对象遍历
var o = {
    name: 'Jack',
    age: 20,
    city: 'Beijing'
};
for (var key in o) {
    if (o.hasOwnProperty(key)) { //判断是否包含KEY
        alert(key); // 'name', 'age', 'city'
    }
}
  • 对数组遍历(遍历 得到的是String而不是number)
var a = ['A', 'B', 'C'];
for (var i in a) {
    alert(i); // '0', '1', '2'
    alert(a[i]); // 'A', 'B', 'C'
}
  • while 和 do..while (和Java基本一样这里不赘述了)

集合

是在ES6以后才引入的
- Map :优点查询快,无论集合多大,检索速度基本不变
-

var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);

m.get('Michael'); // 95
m.set('Tom',65);  //动态设置, 一个key只能对应一个值,
m.has('Tom');  //是否存在KEY, 返回 布尔值
m.delete('Adam');  //删除
  • Set :只储存key,不储存value,同时会把 重复的key自动过滤
var s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"}
s.add(3);
s; // Set {1, 2, 3, "3"}
s.delete(3);
s; // Set {1, 2, "3"}
  • iterable
    • for...of :用法本身跟for...in一样,但是却修复了如下问题
    var a = ['A', 'B', 'C'];
a.name = 'Hello';
for (var x in a) {
    alert(x); // '0', '1', '2', 'name'
}

for (var x of a) {
    alert(x); // 'A', 'B', 'C'
}
  • foreach :
    • 对于set因为没有value,所以下面的element和index是 一致的
    • JS中对于参数可以不一致,所以是可以只穿一个自己需要的参数
var a = ['A', 'B', 'C'];
a.forEach(function (element, index, array) {
    // element: 指向当前元素的值
    // index: 指向当前索引
    // array: 指向Array对象本身
    alert(element);
});

a.forEach(function (element) {
    alert(element);
});

函数

定义

  • 正常定义
    -
function abs(x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
}

function 指出这是一个函数定义;
abs 是函数的名称;
(x) 括号内列出函数的参数,多个参数以,分隔;
{ ... } 之间的代码是函数体,可以包含若干语句,甚至可以没有任何语句。
return 返回值,可以没有:返回undefined
  • 匿名定义
 var abs = function (x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
};
与正常定义完全等价,值已经赋给abs了
注意别忘了最后的 ' ; '

调用

  • 传参 :
    • 可以传多个参数,不过只会使用前面有效的
    • 也可以不传,就等于传入了undefined
function abs(x) {
    //控制传入参数类型
    if (typeof x !== 'number') {
        throw 'Not a number';
    }
  ...
}

argument

  • 定义:关键字arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments类似Array但它不是一个Array
  • 使用:利用arguments,你可以获得调用者传入的所有参数。也就是说,即使函数不定义任何参数,还是可以拿到参数的值
  • 常用于判断传入参数的个数
function abs() {
    if (arguments.length === 0) {
        return 0;
    }
    var x = arguments[0];
    return x >= 0 ? x : -x;
}

abs(); // 0
abs(10); // 10
abs(-9); // 9

rest

  • 定义:ES6中引入的关键字rest,表示获取定义参数以外的参数
function foo(a, b, ...rest) {
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}

foo(1, 2, 3, 4, 5);
// 结果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]

foo(1);
// 结果:
// a = 1
// b = undefined
// Array []

return

  • 由于JavaScript引擎在行末自动添加分号的机制,所以代码如下:
function foo() {
    return; // 自动添加了分号,相当于return undefined;
        { name: 'foo' }; // 这行语句已经没法执行到了
}

应改为
function foo() {
    return { // 这里不会自动加分号,因为{表示语句尚未结束
        name: 'foo'
    };
}

变量作用域

局部作用域:

  • 在函数内定义的变量
  • 这个和java比较相似,就是优先使用最靠近的{}里面的变量
  • 不同的是,for循环中无法定义局部作用域的变量
    所以在ES6中引入了 let,就可以在for中定义了,如下:
function foo() {
    for (var i=0; i<100; i++) {
        //
    }
    i += 100; // 仍然可以引用变量i
}

function foo() {
    var sum = 0;
    for (let i=0; i<100; i++) {
        sum += i;
    }
    i += 1; // 还没定义无法使用,报错
}

变量提升

  • 先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部
function foo() {
    var x = 'Hello, ' + y;
    alert(x);
    var y = 'Bob';
}


//这样的代码实际上在JavaScript眼中是这样的

function foo() {
    var y; // 提升变量y的申明
    var x = 'Hello, ' + y;
    alert(x);
    y = 'Bob';
}

全局作用域

  • 不在函数内定义的变量就是全局的
  • JavaScript默认有一个全局对象window,全局变量都是绑定在window上的一个属性
  • 函数实际也是一个全局变量
var course = 'Learn JavaScript';
alert(course); // 'Learn JavaScript'
alert(window.course); // 'Learn JavaScript'

function foo() {
    alert('foo');
}

foo(); // 直接调用foo()
window.foo(); // 通过window.foo()调用,一样的效果

命名空间

  • 由来:因为全局变量都绑定在 window上,当不同JS文件 使用了相同的变量就会产生冲突
  • 解决:将变量绑定到一个全局变量中
// 唯一的全局变量MYAPP:
var MYAPP = {};

// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;

// 其他函数:
MYAPP.foo = function () {
    return 'foo';
};

常量

  • ES6中引入const关键字表示常量
  • let相同,同时块级作用域

方法

  • 一个对象中绑定函数,成为这个对象的方法

this

  • 特殊变量,始终指向当前对象
  • this指向的问题:
function getAge() {
    var y = new Date().getFullYear();
    return y - this.birth;
}

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: getAge
};

xiaoming.age(); // 25, 正常结果
getAge(); // NaN

var fn = xiaoming.age; // 先拿到xiaoming的age函数
fn(); // NaN

造成上面的原因:this指向动态改变,当在全局调用的时候this就指向了window,显然window没有birth属性。
解决办法:始终 使用 正确对象 . 方法(属性 )
- 关键:函数内定义函数

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        function getAgeFromBirth() {
            var y = new Date().getFullYear();
            return y - this.birth;
        }
        return getAgeFromBirth();
    }
};
xiaoming.age();  //Uncaught TypeError: Cannot read property 'birth' of undefined

//原因:this指向的匿名函数内部也不存在birth
//解决办法:捕获this
age: function () {
        var that = this; // 在方法内部一开始就捕获this
        function getAgeFromBirth() {
            var y = new Date().getFullYear();
            return y - that.birth; // 用that而不是this
        }
        return getAgeFromBirth();
    } 

apply 和 call

  • 用于控制this的指向
  • apply(this指向对象,参数数组)
  • call(this指向对象,参数1,参数2.......)
  • 用法:
function getAge() {
    var y = new Date().getFullYear();
    return y - this.birth;
}

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: getAge
};

xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空
getAge.call(xiaoming,null); 
  • apply的应用:装饰器:
var count = 0;
var oldParseInt = parseInt; // 保存原函数

window.parseInt = function () {
    count += 1;
    return oldParseInt.apply(null, arguments); // 调用原函数
};

// 测试:
parseInt('10');
parseInt('20');
parseInt('30');
count; // 3

高阶函数

  • 一个函数接收另外一个函数作为参数,这种函数就叫高阶函数

Map/Reduce

  • map() : Array本身的方法,传入入的参数是函数对象本身。
  • 用法:
//Array使用map方法 ,传入函数,就可以得到一个新的Array作为结果
var arr=[1,2,3,4];
arr.map(function(x){return x*x;});
arr; // [1, 4, 9, 16]

  • reduce() : 这个方法同样是传入一个函数对象作为参数 但是,规定了函数的参数须为两个
累加应用
var arr = [1, 3, 5, 7, 9];
var result=arr.reduce(function (x, y) {
    return x + y;
}); 
result; // result =25

将数组转换成number
var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
    return x * 10 + y;
}); // 13579

filter

  • 和map() 相似,不同的是根据返回值true or false来决定是否保留该元素
var arr = ['A', 'B', 'C'];
var r = arr.filter(function (element, index, self) {
    console.log(element); // 依次打印'A', 'B', 'C'
    console.log(index); // 依次打印0, 1, 2
    console.log(self); // self就是变量arr
    return element === 'A';
});
r; // ['A']

sort

  • 排序,number自动转换成String,String按照ASCII排序
  • 排序只关心结果, -1 代表小于,0代表等于,1代表大于
  • sort()可以传入函数,实现自定义排序:
因为number会转换成String再按照ASCLL排序,所以默认排序是不能排序数字的
实现数字排序的功能
var arr = [10, 20, 1, 2];
arr.sort(function (x, y) {
    if (x < y) {
        return -1;
    }
    if (x > y) {
        return 1;
    }
    return 0;
}); // [1, 2, 10, 20]

!闭包!

  • 函数内套函数
    • 内函数作为返回值返回的时候
      外部调用获取到的就是内函数的对象 , 需要在外部再次调用才能启动
      同时,内函数持有的外函数内部变量,是在创建的时候就确定了的,外部调用无法改变,可以用来创建有状态的函数
  • 闭包的用途之一是实现对象的私有数据,从而更好地实现OOP
  • 这里会比较绕,具体可以参考一下这篇博客 闭包面试题
    JS面试:闭包
  • 使用:
function lazy_sum(arr) {
    var sum = function () {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    }
    return sum;
}

当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:

var f = lazy_sum([1, 2, 3, 4, 5]); // function sum()
调用函数f时,才真正计算求和的结果:

f(); // 15

循环数的使用例子
function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push((function (n) {
            return function () {
                return n * n;
            }
        })(i));
    }
    return arr;
}

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

f1(); // 1
f2(); // 4
f3(); // 9

  • 这个部分对于没学习过的人会比较不清楚,建议看一下网上的资料学习一下闭包

箭头函数

  • 和匿名函数差不多 ,不过修正了this的指向问题
  • 使用:
x => x*x ; //极简
等同于
function(x){
return x*x;
}

(x, y, ...rest) => {
    if (x > 0) {
        return x * x;
    }
    else {
        return - x * x;
    }
}  //多参数,返回多种情况

x => ({foo:x}) //返回对象

使用箭头函数就可以不在  var that = this;  也可以正确指向对象了
var obj = {
    birth: 1990,
    getAge: function (year) {
        var b = this.birth; // 1990
        var fn = (y) => y - this.birth; // this.birth仍是1990
        //因为箭头函数无法确定this,所call和apply传入第一个参数被忽略了
        return fn.call({bitrh:2000},year);  
    }
};
obj.getAge(); // 25

generator

  • 生成器
  • 与函数类似,不过可以返回多个值
  • 意义:保存变量状态!!!
generator记得在function后面加上*
function* foo(x) {
    yield x++; // yield表示返回值
    yield x++; //每一个yield都会返回一次
    return x++; //return 表示最后的返回
}
调用:
var f = foo(5);//调用generator会返回一个generator对象,而不是直接返回值

f.next(); //  value : 6 , done : false  
//value : 返回值, done : true:false 是否结束标志,在return的时候返回true 
f.next(); // value :7 ,done :false
f.next(); // value :8 ,done : true  
//查看done
f.next().done

for...of 遍历generator
for (var x of foo(5)) {
    console.log(x); // 依次输出6, 7, 8
}

标准对象

不太会总结:

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

var n = new Number(123); // 123,生成了新的包装类型
var b = new Boolean(true); // true,生成了新的包装类型
var s = new String('str'); // 'str',生成了新的包装类型
包装对象的类型是object,不是number也不是String了

Number() , Boolean() , String() 函数可以将任何类型转换,不是转换成包装对象,是转换成Number,String,boolean
对于Boolean()只要是字符串都是true,空字符串才是false

123..toString(); // '123', 注意是两个点!
(123).toString(); // '123'
  • 不要使用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'。

Data

  • 日期和时间
  • JS的月份是0-11,0为1月份,1为2月份
var now = new Date();
now; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)
now.getFullYear(); // 2015, 年份
now.getMonth(); // 5, 月份,注意月份范围是0~11,5表示六月
now.getDate(); // 24, 表示24号
now.getDay(); // 3, 表示星期三
now.getHours(); // 19, 24小时制
now.getMinutes(); // 49, 分钟
now.getSeconds(); // 22, 秒
now.getMilliseconds(); // 875, 毫秒数
now.getTime(); // 1435146562875, 以number形式表示的时间戳

var d = new Date(2015, 5, 19, 20, 15, 30, 123);
d; // Fri Jun 19 2015 20:15:30 GMT+0800 (CST)

RegExp

  • 正则表达式的运用
//创建方法
var re1 = /ABC\-001/;
var re2 = new RegExp('ABC\\-001');
//匹配:
var re = /^\d{3}\-\d{3,8}$/;
re.test('010-12345'); // true
re.test('010-1234x'); // false
re.test('010 12345'); // false
//切分:
'a,b;; c  d'.split(/[\s\,\;]+/); // ['a', 'b', 'c', 'd']
//分组:
var re = /^(\d{3})-(\d{3,8})$/;
re.exec('010-12345'); // ['010-12345', '010', '12345']
re.exec('010 12345'); // null
//特殊标志
//g -全局匹配,  i -忽略大小写 ,  m -多行匹配
具体需要使用可以再去看

JSON

  • JSON在JS中的使用:
var xiaoming = {
    name: '小明',
    age: 14,
    gender: true,
    height: 1.65,
    grade: null,
    'middle-school': '\"W3C\" Middle School',
    skills: ['JavaScript', 'Java', 'Python', 'Lisp']
};

序列化:
JSON.stringify(xiaoming, null, '  ');
结果:
{
  "name": "小明",
  "age": 14,
  "gender": true,
  "height": 1.65,
  "grade": null,
  "middle-school": "\"W3C\" Middle School",
  "skills": [
    "JavaScript",
    "Java",
    "Python",
    "Lisp"
  ]
}
//只输出键name,skills
JSON.stringify(xiaoming, ['name', 'skills'], '  ');
{
  "name": "小明",
  "skills": [
    "JavaScript",
    "Java",
    "Python",
    "Lisp"
  ]
}

//用函数做预处理
function convert(key, value) {
    if (typeof value === 'string') {
        return value.toUpperCase();
    }
    return value;
}

JSON.stringify(xiaoming, convert, '  ');
{
  "name": "小明",
  "age": 14,
  "gender": true,
  "height": 1.65,
  "grade": null,
  "middle-school": "\"W3C\" MIDDLE SCHOOL",
  "skills": [
    "JAVASCRIPT",
    "JAVA",
    "PYTHON",
    "LISP"
  ]
}

//反序列化
JSON.parse('[1,2,3,true]'); // [1, 2, 3, true]
JSON.parse('{"name":"小明","age":14}'); // Object {name: '小明', age: 14}
JSON.parse('true'); // true
JSON.parse('123.45'); // 123.45

JSON.parse('{"name":"小明","age":14}', function (key, value) {
    // 把number * 2:
    if (key === 'name') {
        return value + '同学';
    }
    return value;
}); // Object {name: '小明同学', age: 14}

面向对象编程

基本概述

  • 与Java不同,JS不使用类和实例的概念,JS使用原型来实现OOP
  • JavaScript的原型链和Java的Class区别就在,它没有“Class”的概念
  • 所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。
var Student = {
    name: 'Robot',
    height: 1.2,
    run: function () {
        console.log(this.name + ' is running...');
    }
};

var xiaoming = {
    name: '小明'
};

xiaoming.__proto__ = Student;
//小明的原型指向Student,所以现在小明也可以使用run()
//也就相当与Java的继承

!注意:
不要直接使用  obj.__proto__ 去指向原型,应使用下面这种方法
// 原型对象:
var Student = {
    name: 'Robot',
    height: 1.2,
    run: function () {
        console.log(this.name + ' is running...');
    }
};

function createStudent(name) {
    // 基于Student原型创建一个新对象:
    var s = Object.create(Student);
    // 初始化新对象:
    s.name = name;
    return s;
}

var xiaoming = createStudent('小明');
xiaoming.run(); // 小明 is running...
xiaoming.__proto__ === Student; // true



创建对象

  • 原型链:指定对象属性的时候会根据原型链一层一层往上找,找到了就返回,直到找的是null
var arr = [1, 2, 3];
其原型链是:
arr ----> Array.prototype ----> Object.prototype ----> null
//原型链越长,访问对象属性的时间就越长,所以原型链不应过长
  • 构造函数
function Student(name) {
    this.name = name;
    this.hello = function () {
        alert('Hello, ' + this.name + '!');
    }
}
//使用new来创建对象
var xiaoming = new Student('小明');
xiaoming.name; // '小明'
xiaoming.hello(); // Hello, 小明!
不使用new就返回undefined
使用new就会默认返回this,this指向这个对象本身


使用new 创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身:

xiaoming.constructor === Student.prototype.constructor; // true
Student.prototype.constructor === Student; // true

Object.getPrototypeOf(xiaoming) === Student.prototype; // true

xiaoming instanceof Student; // true

xiaoming.name; // '小明'
xiaohong.name; // '小红'
xiaoming.hello; // function: Student.hello()
xiaohong.hello; // function: Student.hello()
xiaoming.hello === xiaohong.hello; // false

这里不同对象的hello函数是不一样的,不过他们对于我们使用来说应该是一样的才行,所以
function Student(name) {
    this.name = name;
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
};
可以让Student的原型去创造hello函数
这样后面的student对象使用的hello就一样了
  • 比较好的创建方式:
对于忘记使用new的话,this指向的就是window对象,也就是在全局创建了一个name属性
比较合理的创建对象的构造应该是这样的:
function Student(props) {
    this.name = props.name || '匿名'; // 默认值为'匿名'
    this.grade = props.grade || 1; // 默认值为1
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
};

function createStudent(props) {
    return new Student(props || {})
}

原型继承

  • JS中继承上通过原型指向来实现继承
  • 而一般的方法就是创造一个中间对象来实现
  • 重点把握原型指向原型的构造函数修复问题上
// PrimaryStudent构造函数:
function PrimaryStudent(props) {
    Student.call(this, props);
    this.grade = props.grade || 1;
}

// 空函数F:
function F() {
}

// 把F的原型指向Student.prototype:
F.prototype = Student.prototype;

// 把PrimaryStudent的原型指向一个新的F对象,F对象的原型正好指向Student.prototype:
PrimaryStudent.prototype = new F();

// 把PrimaryStudent原型的构造函数修复为PrimaryStudent:
PrimaryStudent.prototype.constructor = PrimaryStudent;

// 继续在PrimaryStudent原型(就是new F()对象)上定义方法:
PrimaryStudent.prototype.getGrade = function () {
    return this.grade;
};

// 创建xiaoming:
var xiaoming = new PrimaryStudent({
    name: '小明',
    grade: 2
});
xiaoming.name; // '小明'
xiaoming.grade; // 2

// 验证原型:
xiaoming.__proto__ === PrimaryStudent.prototype; // true
xiaoming.__proto__.__proto__ === Student.prototype; // true

// 验证继承关系:
xiaoming instanceof PrimaryStudent; // true
xiaoming instanceof Student; // true
  • 简要封装:
//实现对象之间的继承函数
function inherits(Child, Parent) {
    var F = function () {};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
}

class继承

  • 在ES6中才引入的关键词class
  • 作用:简化继承,减少代码量
  • 与Java基本一致,比较容易理解
//使用class创建对象
class Student {
    constructor(name) {
        this.name = name;
    }

    hello() {
        alert('Hello, ' + this.name + '!');
    }
}

//继承的使用
class PrimaryStudent extends Student {
    constructor(name, grade) {
        super(name); // 记得用super调用父类的构造方法!
        this.grade = grade;
    }

    myGrade() {
        alert('I am at grade ' + this.grade);
    }
}


浏览器

概述

主要浏览器:
- IE 6~11:国内用得最多的IE浏览器,历来对W3C标准支持差。从IE10开始支持ES6标准;

  • Chrome:Google出品的基于Webkit内核浏览器,内置了非常强悍的JavaScript引擎——V8。由于Chrome一经安装就时刻保持自升级,所以不用管它的版本,最新版早就支持ES6了;
  • Safari:Apple的Mac系统自带的基于Webkit内核的浏览器,从OS X 10.7 Lion自带的6.1版本开始支持ES6,目前最新的OS X 10.11 El Capitan自带的Safari版本是9.x,早已支持ES6;

  • Firefox:Mozilla自己研制的Gecko内核和JavaScript引擎OdinMonkey。早期的Firefox按版本发布,后来终于聪明地学习Chrome的做法进行自升级,时刻保持最新;

  • 移动设备上目前iOS和Android两大阵营分别主要使用Apple的Safari和Google的Chrome,由于两者都是Webkit核心,结果HTML5首先在手机上全面普及(桌面绝对是Microsoft拖了后腿),对JavaScript的标准支持也很好,最新版本均支持ES6。

需要注意:某某安全浏览器,某某旋风浏览器,它们只是做了一个壳,其核心调用的是IE

对于编写JavaScript的时候,就要充分考虑到浏览器的差异,尽量让同一份JavaScript代码能运行在不同的浏览器中


浏览器对象

  • window:
    • 不但充当全局作用域,也表示浏览器窗口
    • innerWidth属性:获取浏览器窗口内部的宽度
    • innerHeight属性:获取浏览器窗口内部的高度
    • outerWidth属性:获取浏览器窗口整个的宽度
    • outerHeight属性:获取浏览器窗口整个高度
  • navigator:
    • 浏览器 信息对象
    • navigator.appName:浏览器名称;
    • navigator.appVersion:浏览器版本;
    • navigator.language:浏览器设置的语言;
    • navigator.platform:操作系统类型;
    • navigator.userAgent:浏览器设定的User-Agent字符串。
    • navigator信息容易被修改,为了针对不同浏览器 :
    var width;
if (getIEVersion(navigator.userAgent) < 9) {
    width = window.innerWidth || document.body.clientWidth;
}
  • screen:
    • 屏幕信息对象
    • screen.width:屏幕宽度,以像素为单位;
    • screen.height:屏幕高度,以像素为单位;
    • screen.colorDepth:返回颜色位数,如8、16、24。
  • location:
    • 当前页面的URL信息对象
location.href; //http://www.example.com:8080/path/index.html?a=1&b=2#TOP
location.protocol; // 'http'
location.host; // 'www.example.com'
location.port; // '8080'
location.pathname; // '/path/index.html'
location.search; // '?a=1&b=2'
location.hash; // 'TOP'

`location.assgin('/di');` //加载新页面
`location.reload();` //重新加载当前页面
  • document
    • 由于HTML在浏览器中以DOM形式表示为树形结构,document对象就是整个DOM树的根节点。
    • document的title就是HTML中的'title内容读取document.title='hahahha';
    • document.getElementById(id) 根据传入id获取节点对象
    • document.getElementsByTagName() 根据 TagName读取节点对象
    • document.cookie; 获取当前页面的cookie信息
  • history
    • 浏览器的历史记录信息对象
    • history.forward() 前进
    • history.back() 后退
    • 任何时候都不应使用history对象

操作DOM

概述:

  • 更新:更新该DOM节点的内容,相当于更新了该DOM节点表示的HTML的内容;
  • 遍历:遍历该DOM节点下的子节点,以便进行进一步操作;

  • 添加:在该DOM节点下新增一个子节点,相当于动态增加了一个HTML节点;

  • 删除:将该节点从HTML中删除,相当于删掉了该DOM节点的内容以及它包含的所有子节点。

  • 代码常用做法:
// 返回ID为'test'的节点:
var test = document.getElementById('test');

// 先定位ID为'test-table'的节点,再返回其内部所有tr节点:
var trs = document.getElementById('test-table').getElementsByTagName('tr');

// 先定位ID为'test-div'的节点,再返回其内部所有class包含red的节点:
var reds = document.getElementById('test-div').getElementsByClassName('red');

// 获取节点test下的所有直属子节点:
var cs = test.children;

// 获取节点test下第一个、最后一个子节点:
var first = test.firstElementChild;
var last = test.lastElementChild;

//版本<IE8是不支持下面功能的
// 通过querySelector获取ID为q1的节点:
var q1 = document.querySelector('#q1');

// 通过querySelectorAll获取q1节点内的符合条件的所有节点:
var ps = q1.querySelectorAll('div.highlighted > p');

更新DOM

  • innerHTML方式修改
// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置文本为abc:
p.innerHTML = 'ABC'; // <p id="p-id">ABC</p>
// 设置HTML:
p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ';
// <p>...</p>的内部结构已修改
  • innerText或textContent方式修改
// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置文本:
p.innerText = '<script>alert("Hi")</script>';
// HTML被自动编码,无法设置一个<script>节点:
// <p id="p-id"><script>alert("Hi")</script></p>

版本<IE9是不支持textContent
  • 修改节点的css样式
// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置CSS:
p.style.color = '#ff0000';
p.style.fontSize = '20px';
p.style.paddingTop = '2em';

插入DOM

  • appendChild(节点对象) :将子节点添加到到父节点最后
  • createElement(什么节点) :创建一个新的节点
  • insertBefore(newElement, referenceElement);插入节点
<!-- HTML结构 -->
<p id="js">JavaScript</p>
<div id="list">
    <p id="java">Java</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
</div>

var
    js = document.getElementById('js'),//获取节点
    list = document.getElementById('list');
list.appendChild(js);//在尾部添加节点

var 
    python=document.getElementById('python');//获取python节点
    haskell = document.createElement('p'); //创建节点
haskell.id = 'haskell'; //设置节点属性
haskell.innerText = 'Haskell';
list.innerBefore(haskell,python);

<!-- HTML结构 -->
<div id="list">
    <p id="java">Java</p>
    <p id="haskell">Haskell</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
    <p id="js">JavaScript</p>
</div>

删除DOM

  • 删除节点以后节点数动态变化
  • child.parentElement; 获取当前节点的父节点
  • parent.removeChild(child); 获取父节点然后调用remove传入要删除的节点对象

操作表单

  • HTML表单中的输入控件:

    • 文本框,对应的<input type="text">,用于输入文本;
    • 口令框,对应的<input type="password">,用于输入口令;

    • 单选框,对应的<input type="radio">,用于选择一项;

    • 复选框,对应的<input type="checkbox">,用于选择多项;

    • 下拉框,对应的<select>,用于选择一项;

    • 隐藏文本,对应的<input type="hidden">,用户不可见,但表单提交时会把隐藏文本发送到服务器。

  • 获取与修改值:

// <label><input type="radio" name="weekday" id="monday" value="1"> Monday</label>
// <label><input type="radio" name="weekday" id="tuesday" value="2"> Tuesday</label>
var mon = document.getElementById('monday');
var tue = document.getElementById('tuesday');
mon.value; // '1'
tue.value; // '2'
//对于选框要使用checked来获取
mon.checked; // true或者false
tue.checked; // true或者false
  • 提交表单;
    • 1、调用表单对象的submit(),不过会影响了form的正常提交
    • 2、响应form的onSubmit事件
!-- HTML -->
<form id="test-form" onsubmit="return checkForm()">
    <input type="text" name="test">
    <button type="submit">Submit_Two</button>
    <button type="button" onclick="doSubmitForm()">Submit_One</button>
</form>

//第一种方式
<script>
function doSubmitForm() {
    var form = document.getElementById('test-form');
    // 可以在此修改form的input...
    // 提交form:
    form.submit();
}
</script>

//第二种港式
<script>
function checkForm() {
    var form = document.getElementById('test-form');
    // 可以在此修改form的input...
    // 继续下一步:
    return true;
}
</script>

操作文件

  • 获取文件url:
var f = document.getElementById('test-file-upload');
var filename = f.value; // 'C:\fakepath\test.png'
  • H5新增的File API提供了对File文件操作的支持
var
    fileInput = document.getElementById('test-image-file'),
    info = document.getElementById('test-file-info'),
    preview = document.getElementById('test-image-preview');
// 监听change事件:
fileInput.addEventListener('change', function () {
    // 清除背景图片:
    preview.style.backgroundImage = '';
    // 检查文件是否选择:
    if (!fileInput.value) {
        info.innerHTML = '没有选择文件';
        return;
    }
    // 获取File引用:
    var file = fileInput.files[0];
    // 获取File信息:
    info.innerHTML = '文件: ' + file.name + '<br>' +
                     '大小: ' + file.size + '<br>' +
                     '修改: ' + file.lastModifiedDate;
    if (file.type !== 'image/jpeg' && file.type !== 'image/png' && file.type !== 'image/gif') {
        alert('不是有效的图片文件!');
        return;
    }
    // 读取文件:
    var reader = new FileReader();
    reader.onload = function(e) {
        var
            data = e.target.result; // 'data:image/jpeg;base64,/9j/4AAQSk...(base64编码)...'            
        preview.style.backgroundImage = 'url(' + data + ')';
    };
    // 以DataURL的形式读取文件:
    reader.readAsDataURL(file);
});
  • 回调:读取文件一般都是异步的操作:
reader.readAsDataURL(file);
reader.onload = function(e) {
    // 当文件读取完成后,自动调用此函数:
};

AJAX

AJAX: Asynchronous JavaScript and XML :用JavaScript执行异步网络请求
这里直接跳到网页去看吧:
走你: AJAX


Promise

用于封装异步操作,以便根据异步操作是否成功来进行后续的操作。
走你:Promise


Canvas

  • HTML5新增的组件,用于绘制动画图标等
  • HTML中canvas的声明定义
  • canvas的获取
  • canvas的使用 : 这里的使用和Android中十分相似,很好理解
  • canvas绘制坐标如下

坐标

canvas在HTML中的定义
<canvas id="test-stock" width="300" height="200">
    <p>Current Price: 25.51</p>//浏览器对H5的支持不一致,所以最好加上说明性代码,这一句就是,如果浏览器支持,就会忽略这一句代码
</canvas>

var ctx = canvas.getContext('2d');//获取2Dcanvas

var gl = canvas.getContext("webgl");//获取3D canvas

ctx.clearRect(0, 0, 200, 200); // 擦除(0,0)位置大小为200x200的矩形,擦除的意思是把该区域变为透明
ctx.fillStyle = '#dddddd'; // 设置颜色
ctx.fillRect(10, 10, 130, 130); // 把(10,10)位置大小为130x130的矩形涂色
// 利用Path绘制复杂路径:
var path=new Path2D();
path.arc(75, 75, 50, 0, Math.PI*2, true);
path.moveTo(110,75);
ctx.strokeStyle = '#0000ff'; //颜色
ctx.stroke(path); //将路径画在canvas上

//绘制文本
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 2;
ctx.shadowColor = '#666666';
ctx.font = '24px Arial';
ctx.fillStyle = '#333333';
ctx.fillText('带阴影的文字', 20, 40);

总结

对于JavaScript的基础学习就到这里了,这个系列只是简单的让大家对JS有一个快速的认知和掌握,具体在开发中还是要慢慢琢磨,希望对在做安卓开发又想拓展一下前端知识的你有一点点帮助。
至于前端后面的一些框架的学习使用的笔记,近期是没有写的打算了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注