系统分析与设计hw3
这几周学习了一下javascript,以学习笔记作为技术报告:
JavaScript学习笔记
1.数据类型和变量
数据类型:
Number、字符串、布尔值
变量声明使用var
,因为js是动态语言
比较运算符:
===
的使用,此运算符不会做自动类型转换,而==
则会。一般用===
;
NaN
和所有值比较都不相等,包括它自己。唯一判断方法为isNaN
;
浮点数之间的相等比较只能用Math.abs(1/3-(1-2/3)) < 0.00000000001
;
null
表示一个空的值,常用;undefined
表示未定义的值,仅仅在判断函数参数是否传递的情况有用;
求幂运算符:
Es7:**
;Es6:Math.pow(x,y)
;
数组:
一个数组可以包括任意数据类型;
初始化:用[]
或者new Array()
;
var arr = [1, 2, 3.14, 'Hello', null, true];
arr[0]; // 返回索引为0的元素,即1
arr[5]; // 返回索引为5的元素,即true
arr[6]; // 索引超出了范围,返回undefined
对象:
var person = {
name: 'Bob',
age: 20,
tags: ['js', 'web', 'mobile'],
city: 'Beijing',
hasCar: true,
zipcode: null
};
strict模式:
使用'use strict'
可以避免不是用var导致的全局变量
2.字符串
多行字符串:
`这是一个
多行
字符串`;
模板字符串:
非模板字符串:var message = '你好, ' + name + ', 你今年' + age + '岁了!';
模板字符串:var message = `你好, ${name}, 你今年${age}岁了!`;alert(message);
操作字符串:
长度:
s.length;
下标操作:(但对索引赋值没用,不会改变原字符串),所以只能重新赋值
s[0];
转小写和大写:
s.toUpperCase(); // 返回'HELLO'
var lower = s.toLowerCase();
查找子串出现的位置:
var s = 'hello, world';
s.indexOf('world'); // 返回7
s.indexOf('World'); // 没有找到指定的子串,返回-1
返回指定索引区间的子串:
var s = 'hello, world'
s.substring(0, 5); // 从索引0开始到索引5(不包括5),返回'hello'
s.substring(7); // 从索引7开始到结束,返回'world'
字符串分割并返回数组:
"2:3:4:5".split(":") //将返回["2", "3", "4", "5"]
"|a|b|c".split("|") //将返回["", "a", "b", "c"]
字符串转数字:
parseInt(string, radix);
//只有字符串中的第一个数字会被返回。
//开头和结尾的空格是允许的。
parseInt("10"); //返回 10
parseInt("19",10); //返回 19 (10+9)
parseInt("11",2); //返回 3 (2+1)
parseInt("17",8); //返回 15 (8+7)
parseInt("1f",16); //返回 31 (16+15)
parseInt("010"); //未定:返回 10 或 8
参数 | 描述 |
---|---|
string | 必需。要被解析的字符串。 |
radix | 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。 如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。 如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。 |
去除字符串两端的空白:
string.trim();
3.数组
length
属性可以修改,增加的值为undefined;
索引
无上界,但建议不要越界;
slice()
:相当于字符串的substring()
,新特点是“若没有参数,则会从头到尾截取所有元素”
push()和pop()
:往尾部添加信息,从尾部弹出信息
shift()和unshift()
:前者删除头部信息,后者从头部插入信息
arr.sort()
:排序
arr.reverse()
:反转
splice()
:增加删除的万能函数
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()
:用特定字符串链接元素,返回字符串;
多维数组:var arr = [[1, 2, 3], [400, 500, 600], '-'];//要用二维索引
4.对象
有效变量用.
,无效变量要用['xxx']
添加删除对象属性(注意,属性间用逗号分开,而且最后一个属性不用逗号,对象声明后要用分号):
var xiaoming = {
name: '小明'
};
xiaoming.age; // undefined
xiaoming.age = 18; // 新增一个age属性
xiaoming.age; // 18
delete xiaoming.age; // 删除age属性
xiaoming.age; // undefined
delete xiaoming['name']; // 删除name属性
xiaoming.name; // undefined
delete xiaoming.school; // 删除一个不存在的school属性也不会报错
in
:检测该对象是否包含该属性,或者其父类是否包含该属性
'name' in xiaoming; // true
hasOwnProperty()
:只检测该对象是否包含该属性
xiaoming.hasOwnProperty('name'); // true
xiaoming.hasOwnProperty('toString'); // false
遍历该对象所有属性:(for…in循环, 数组也可以用这个,但key不是number,而是string)
for(var key in xm){
console.log('key:'+key+'|value:'+xm[key])
}
5.Map
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);//第一种初始化,用二维数组
var m = new Map(); // 第二种初始化:空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined
6.Set
var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3
s1.add(4);
s1.delete(4);
7.iterable
包括Array、Map、Set
遍历方式有两种:
for ... of:
var a = ['A', 'B', 'C'];
var s = new Set(['A', 'B', 'C']);
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
for (var x of a) { // 遍历Array
console.log(x);
}
for (var x of s) { // 遍历Set
console.log(x);
}
for (var x of m) { // 遍历Map
console.log(x[0] + '=' + x[1]);
}
foreach:
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) {
console.log(element);
});
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
console.log(value);
});
var a = ['A', 'B', 'C'];
a.forEach(function (element, index, array) {
console.log(element + ', index = ' + index);
});
8.函数定义和调用:
两种声明和定义方式:
function abs(x) {}
var abs = function (x) {};//匿名函数(函数是对象,而abs为指向给该函数的对象)
关键字arguments:
获得调用者传入的所有参数。即使函数不定义任何参数,还是可以拿到参数的值;最常用于判断传入参数的个数,可以把某些参数变成“可选”参数:
function foo(a, b, c) {
if (arguments.length === 2) {
// 实际拿到的参数是a和b,c为undefined
c = b; // 把b赋给c
b = null; // b变为默认值
}
}
关键字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 ]
注意:
用return
的时候注意js会在行末自动添加分号,所以return的内容必须和return本身同一行
9.变量作用域与解构赋值
变量提升:
它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,但并不赋值;由于JavaScript的这一怪异的“特性”,我们在函数内部定义变量时,请严格遵守在函数内部首先申明所有变量这一规则。
function foo() {
var
x = 1, // x初始化为1
y = x + 1, // y初始化为2
z, i; // z和i为undefined
// 其他语句:
}
全局作用域:
不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window
,全局作用域的变量实际上被绑定到window
的一个属性,包括顶层函数也是window
的一个属性。
任何变量(函数也视为变量),如果没有在当前函数作用域中找到,就会继续往上查找,最后如果在全局作用域中也没有找到,则报ReferenceError
错误。
var course = 'Learn JavaScript';
alert(course); // 'Learn JavaScript'
alert(window.course); // 'Learn JavaScript'
function foo() {
alert('foo');
}
window.foo(); // 通过window.foo()调用
名字空间:为了减少全局变量或者函数命名冲突;
减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。例如:
/ 唯一的全局变量MYAPP:
var MYAPP = {};
// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函数:
MYAPP.foo = function () {
return 'foo';
};
局部作用域:
由于JavaScript的变量作用域实际上是函数内部,我们在for
循环等语句块中是无法定义具有局部作用域的变量的,为了解决块级作用域,ES6引入了新的关键字let
,用let
替代var
可以申明一个块级作用域的变量:
function foo() {
var sum = 0;
for (let i=0; i<100; i++) {
sum += i;
}
// SyntaxError:
i += 1;
}
常量:
ES6标准引入了新的关键字const
来定义常量,const
与let
都具有块级作用域
解构赋值:
var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前两个元素,只对z赋值第三个元素
let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];//嵌套层次和位置要保持一致
//利用解构赋值快速获取对象的指定属性,同样适用于嵌套的对象
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678',
address: {
city: 'Beijing',
street: 'No.1 Road',
zipcode: '100001'
}
};
var {name, age, passport} = person;
var {name, address: {city, zip}} = person;
// 注意: address不是变量,而是为了让city和zip获得嵌套的address对象的属性:
address; // Uncaught ReferenceError: address is not defined
// 把passport属性赋值给变量id:
let {name, passport:id} = person;
// 如果person对象没有single属性,默认赋值为true:
var {name, single=true} = person;
10.方法
定义:绑定到对象上的函数称为方法
this
:this是js设计的重大失误,其指向很不明确,只能使用obj.function
才会指向该对象,即使在‘use strict’的模式下也只能通过指向undefined
来修正错误;
下面为正确使用this的方法:
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var that = this; // 在方法内部一开始就捕获this
var y = new Date().getFullYear();
return y - that.birth;
}
};
xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了
第二种方法,使用apply
或者call
:apply使用array传入函数参数,而call则是按顺序传入函数参数;对于普通函数,this绑定为null
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, 参数为空
Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5
装饰器:例子为一共调用了多少次parseInt()
var count = 0;
var oldParseInt = parseInt; // 保存原函数
window.parseInt = function () {
count += 1;
return oldParseInt.apply(null, arguments); // 调用原函数
};
11.高阶函数
定义:在一个函数中接收另一个函数作为参数
map()
//arr.map(function(currentValue,index,arr),thisValue) 当前元素值,元素索引,数组对象
function pow(x) {
return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var results = arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]
reduce()
//效果相当于:[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
//下面例子,对array中的元素求和
var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
return x + y;
}); // 25
//下面例子,将数组转成一个数字
var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
return x * 10 + y;
}); // 13579
filter()
//和map一样,传入的function作用于每个函数
//例子为挑选数组中的奇数
var arr = [1, 2, 4, 5, 6, 9, 10, 15];
var r = arr.filter(function (x) {
return x % 2 !== 0;
});
r; // [1, 5, 9, 15]
**sort():**sort()方法会直接对array进行修改,返回结果仍然是当前array
//js中字符串的默认排序方式为按ASCII码排序
//数组自带的sort排序会将元素转换成字符串再比较,所以不能用自带的sort对数字、字符串进行排序
//下面例子为正确排序数字
arr.sort(function (x, y) {
if (x < y) {
return -1;
}
if (x > y) {
return 1;
}
return 0;
});
//下面例子为正确排序字符串(先全部转为大写或小写)
arr.sort(function (s1, s2) {
x1 = s1.toUpperCase();
x2 = s2.toUpperCase();
if (x1 < x2) {
return -1;
}
if (x1 > x2) {
return 1;
}
return 0;
});
12.闭包
定义:函数作为返回值
举例:在这个例子中,我们在函数lazy_sum
中又定义了函数sum
,并且,内部函数sum
可以引用外部函数lazy_sum
的参数和局部变量,当lazy_sum
返回函数sum
时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
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
封装私有变量:
//无法访问到x
function create_counter(initial) {
var x = initial || 0;
return {
inc: function () {
x += 1;
return x;
}
}
}
var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3
13.箭头函数
定义:相当于匿名函数
(x, y) => x * x + y * y
// 无参数:
() => 3.14
// 可变参数:
(x, y, ...rest) => {
var i, sum = x + y;
for (i=0; i<rest.length; i++) {
sum += rest[i];
}
return sum;
}
//返回对象
x => ({ foo: x })
箭头函数的this问题
//现在,箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj:
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
return fn();
}
};
obj.getAge(); // 25
14.标准对象
15.Date对象
16.面向对象编程
在编写JavaScript代码时,不要直接用obj.__proto__
去改变一个对象的原型,并且,低版本的IE也无法使用__proto__
。Object.create()
方法可以传入一个原型对象,并创建一个基于该原型的新对象,但是新对象什么属性都没有
// 原型对象:
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