JavaScript基础
主要记录和C、python的语法差异
前言
1、strict模式
1、进行变量声明的时候,可以不用var。但是如果不声明,这个变量就会变成全局变量。所以为了让变量在局部起作用,需要进入到strict模式,在strict模式下运行的JavaScript代码,强制通过var申明变量,未使用var申明变量就使用的,将导致运行错误。
2、启用strict模式的方法是在JavaScript代码的第一行写上:
'use strict';
2、js代码书写位置
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
//js代码写在<script>标签中。<script>标签包含的js代码可以放在head\body\另外的js文件中。
// <script src="xx.js"></script> 引入外部的js文件
<script>
function myFunction()
{
document.getElementById("demo").innerHTML="yyx";
}
</script>
</head>
<body>
<h1>web</h1>
<p id="demo">段落</p>
<button type="button" onclick="myFunction()">尝试</button>
</body>
</html>
3、浏览器调试代码
(1)在网页当中打开开发者模式,在console中,类似终端的地方,可以直接输入脚本语言,即写在script当中的语句。
(2)或者可以创建一个小的脚本文件,在sources – snippets里面创建
4、输出
输出可以在实现前端代码的任意位置进行
(1)alert() — 以警告栏的形式输出
(2)document.write() – 向html文档中写入,即显示到页面上
(3)getElementById + innerHTML – 选中HTML标签并且取代其值,写入到HTML元素当中。
(4)console.log() – 主要用来调试
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<p id="demo"></p>
<script>
var x, y, z;
x = 5;
y = 6;
z = (x + y) * 10;
// 通常的在网页当中的输出的写法
document.getElementById("demo").innerHTML = z;
</script>
</body>
</html>
一、对象
JavaScript 提供多个内建对象,比如 String、Date、Array 等等。基本的{...}定义的对象。 对象只是带有属性和方法的特殊数据类型。
可以进行不同数据类型之间的转换,有以下规则:
- 不要使用
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'。
// 将字符串转换成整型
var n = Number('123'); // 一般不用这个
var n = parseInt('123')
typeof n; // 'number'
1、定义通常意义上的对象、属性、以及对象的方法
var person = {
name: 'Bob',
age: 20,
zipcode: null,
// 特殊的属性名
'middle-school': 'No.1 Middle School',
// 对象的方法
fullName : function()
{
return this.name + " " + this.age;
}
};
注意:
(1)属性名其实都是字符串类型的,属性对应的值是任意数据类型.
(2)对于特殊的属性名,是需要用’ ’ 括起来的。
2、访问对象的属性以及方法
// 正常的属性名
person.name;
// 特殊的属性名
preson['middle-school'];
// 方法
person.fullName();
3、属性的有关操作
// 删除属性
delete person.age;
// 添加属性
person.class = one;
// 检查是否存在
# 包括继承的属性,注意属性是字符串形式
'name' in person;
// 不包括继承的属性
person.hasOwnProperty('name');
4、方法 – this特殊变量
- 在方法中,this 表示该方法所属的对象。
- 如果单独使用,this 表示全局对象。
- 在函数中,this 表示全局对象。
- 在函数中,在严格模式下,this 是未定义的(undefined)。
- 在事件中,this 表示接收事件的元素。
- 类似 call() 和 apply() 方法可以将 this 引用到任何对象。
(1)this变量的指向
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
// this指向被调用的对象xiaoming
xiaoming.age(); // 25, 正常结果
// this变量指向全局对象window(非strict模式) -- 不会报错
// this变量指向define(strict模式) -- 会报错
getAge(); // NaN
(2)改变this的指向 – apply() call()
apply方法 + call方法
它接收两个参数,第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数本身的参数。
apply()把参数打包成Array再传入;call()把参数按顺序传入。
// this指向xiaoming, 参数为空
getAge.APPLY(xiaoming,[])
// apply 和 call方法的差距
Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5
(3)闭包中使用this
this指针只在age方法的函数内指向xiaoming,在函数内部定义的函数,this又指向undefined了。
'use strict';
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
// 把外部作用域中的 this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了。在方法内部一开始就捕获this.
var that = this;
// 内部函数,是一个闭包
function getAgeFromBirth() {
var y = new Date().getFullYear();
// this在严格模式下指向undefined,非严格模式下指向window
// return y - this.birth;
return y - that.birth;
}
return getAgeFromBirth();
}
};
xiaoming.age(); // Uncaught TypeError: Cannot read property 'birth' of undefined
5、内建对象
(1)Data
a.时间戳
时间戳是一个自增的整数,它表示从1970年1月1日零时整的GMT时区开始的那一刻,到现在的毫秒数。假设浏览器所在电脑的时间是准确的,那么世界上无论哪个时区的电脑,它们此刻产生的时间戳数字都是一样的,所以,时间戳可以精确地表示一个时刻,并且与时区无关。
b.获取时间
注意:JavaScript的Date对象月份值从0开始,牢记0=1月,1=2月,2=3月,……,11=12月。
// 获取系统当前时间
var now = new Data();
now.getTime(); // 以number形式表示的时间戳
// 用时间戳转换成Data
var d = new Date(1435146562875);
d; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)
(2)RegExp正则表达式
注意:用一种描述性的语言给字符串定义一个规则,用这个规则取检查字符串是否合法。
// 固定字符
\d匹配数字 \w匹配字母或者数字 .匹配任意字符 \s可以匹配一个空格(也包括Tab等空白符)
// 变长字符
用*表示任意个字符(包括0个),用+表示至少一个字符,用?表示0个或1个字符,用{n}表示n个字符,用{n,m}表示n-m个字符
// 特殊字符
由于'-'是特殊字符,在正则表达式中,要用'\'转义
// 表示范围
可以用[]表示范围,[0-9a-zA-Z\_]可以匹配一个数字、字母或者下划线;
// 指定开头和结尾
^表示行的开头,^\d表示必须以数字开头。
$表示行的结束,\d$表示必须以数字结束。
^xxx$ -- 表示xxx这一个数字
\d{3}\s+\d{3,8} // 匹配以任意个空格隔开的带区号的电话号码
a. 使用正则表达式 (默认使用贪婪匹配)
//创建 -- 第一种方式是直接通过/正则表达式/写出来,第二种方式是通过new RegExp('正则表达式')创建一个RegExp对象。
var re1 = /ABC\-001/;
var re2 = new RegExp('ABC//-001'); // 字符串的转义问题,字符串的两个\\实际上是一个\
// 检查匹配 -- RegExp对象的test()方法用于测试给定的字符串是否符合条件。
re1.test('ABC-001') //true
b. 利用正则表达式切分字符串
// 传统的空格区分不足以识别连续的空格
'a b c'.split(''); // ['a', 'b', '', '', 'c']
// 正则
'a b c'.split(/\s+/); // ['a', 'b', 'c']
c. 利用正则表达式将字符串进行分组
// ()表示的就是要提取的分组(Group),用exec()方法提取出子串来
// ^(\d{3})-(\d{3,8})$分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码
var re = /^(\d{3})-(\d{3,8})$/;
re.exec('010-12345'); // ['010-12345', '010', '12345']
re.exec('010 12345'); // null
(3)JSON
1、JSON(JavaScript Object Notation),这是一种数据交换格式,用于把任何JavaScript对象变成JSON,就是把这个对象序列化成一个JSON格式的字符串,这样才能够通过网络传递给其他计算机。
2、在JavaScript中,我们可以直接使用JSON,因为JavaScript内置了JSON的解析。JSON还定死了字符集必须是UTF-8,表示多语言就没有问题了。为了统一解析,JSON的字符串规定必须用双引号"",Object的键也必须用双引号""。
3、J对象和JSON的相互转换
对象转JSON
var xioaming = {
name: '小明',
age: 14,
gender: true
}
// 第二个参数用于控制如何筛选对象的键值.可以传入数组也可以是函数
// 第三个参数用来控制输出的格式
var s = JSON.stringify(xiaoming, null, ' ');
console.log(s);
// JSON格式
{
"name": "小明",
"age": 14,
"gender": true,
"height": 1.65,
"grade": null,
"middle-school": "\"W3C\" Middle School",
"skills": [
"JavaScript",
"Java",
"Python",
"Lisp"
]
}
JSON转对象
JSON.parse('[1,2,3,true]'); // [1,2,3,true]
二、面向对象编程
1、基于原型 prototype去创建一个新的对象 – 原型继承
(1)原型链
JS的一切事物都是对象,而且会对每一个对象都设置一个原型,指定它的原型对象。所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
一个对象的原型链:当前对象 – > 原型对象 --> Object.prototype对象 --> undefined
var arr = [1, 2, 3];
// arr ----> Array.prototype ----> Object.prototype ----> null
由于arr的原型Array.prototype定义了indexOf()、shift()等方法,因此你可以在所有的Array对象上直接调用这些方法。
(2)使用Object.create()方法 – 一般不使用
JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。即当想要创建一个新的对象,而且这个对象没有相应的类型使用时,就可以修改这个新对象的原型,使用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);
// 初始化新对象
a.name = name;
return s;
}
// 创建基于Student原型的小明对象
var xiaoming = createStudent('小明')
xiaoming.run(); // 小明 is running...
// 小明对象的原型就是Student
xiaoming.__proto__ === Studnet; // true
(3)用new() 创建基于原型的JavaScript的对象
a.构造函数:
用关键字new来调用这个函数,并返回一个对象。那么这个函数就是构造函数。
如果不使用new,那么Student里的this是指向undefine.当使用new的时候,它就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this。
// 使用函数构造对象
function Student(name){
this.name = name;
this.hello = function(){
alert('Hello, ' + this.name + '!');
}
}
// 使用关键字new去调用函数,创建对象的实例
var xiaoming = new Student('小明');
xiaoming.name;
// xiaoming的原型链:xiaoming ----> Student.prototype ----> Object.prototype ----> null。
new Student()创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身。为了让创建的对象共享一个hello函数,把hello函数移动到xiaoming、xiaohong这些对象共同的原型上就可以了,也就是`Student.prototype。
b.prototype
一般是不允许给一个已存在构造器的对象中是不能添加新的属性,要添加一个新的属性需要在在构造器函数中添加。
// 不允许
Student.nationality = "English";
但是使用 prototype 属性就可以给对象的构造函数添加新的属性
// 允许
Student.prototype.nationality = "English";
c.常用的编程模式
编写一个createStudent()函数,在内部封装所有的new操作。优点:一是不需要new来调用,二是参数非常灵活,可以不传。
// Student构造函数
function Student(props){
this.name = props.name || 'Unnamed';
this.grade props.grade || 1 ;
}
// 将hello函数移动到Student.prototype,使得由student创建的对象共享一个hello函数
Student.prototype.hello = function(){
alert('Hello, ' + this.name + '!');
};
function createStudent(props){
return new Student(props || {})
}
//调用,不需要new
var xixaoming = creatStudent({
name: '小明'
})
(4)原型继承
// student构造函数 -- 同上
// 基于Studnet扩展出PrimaryStudent,即要继承Student。原型链变成new PrimaryStudent() ----> PrimaryStudent.prototype ----> (Student.prototype )----> Object.prototype ----> null
//PrimaryStudent构造函数
function PrimaryStudent(props){
// 调用希望“继承”的构造函数Student,绑定this变量:
Student.call(this,props);
this.grade = props.grade || 1;
}
/*
// 空函数F,利用这个中间对象改变原型链.PrimaryStudent() ----> PrimaryStudent.prototype ----> F -- >Student.prototype----> Object.prototype ----> null
function F() {
}
// 把F的原型指向Student.prototype
F.prototype = Student.prototype;
//把PrimaryStudent的原型指向一个新的F对象
PrimaryStudent.prototype = new F();
// 把PrimaryStudent原型的构造函数修复为PrimaryStudent:
PrimaryStudent.prototype.constructor = PrimaryStudent;
*/
// 可以将继承这个动作进行封装
function inherits(Child,Parent){
var F = funtion() {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
}
// 实现原型继承链
inherits(PrimaryStudent, Student);
// 继续在PrimaryStudent原型(就是new F()对象)上定义方法:
PrimaryStudent.prototype.getGrade = function () {
return this.grade;
};
// xiaoming是基于原型PrimaryStudent创建的对象,PrimaryStudent是继承student的。
var xiaoming = new PrimaryStudent({
name: '小明',
grade: 2
});
xiaoming.name; // '小明'
xiaoming.grade; // 2
2、class继承
(1)类的写法
类是用于创建对象的模板。类声明和类表达式的主体都执行在严格模式下。所有变量都必须声明。
// 以前的写法
function Student(name){
this.name = name;
}
Student.prototype.hello = function(){
alert()
}
a.第一种
// 定义一个class类
class Student{
//定义类的构造函数constructor(),用来初始化对象属性。
constructor(name){
this.name = name;
}
//定义在原型对象上的hello()方法
hello(){
alert('Hello, ' + this.name +'!');
}
}
// 使用new关键字创建一个Student对象
var xiaoming = new Student('小明');
xiaoming.hello();
b.第二种 – 类表达式
// 匿名类
let Runoob = class{
constructor(name,url){
this.name = name;
this.url = url;
}
};
// 命名类
let Runoob = class Ruoob2{
constructor(name,url){
this.name = name;
this.url = url;
}
}
(2)class继承 – extend
依据一个类来定义一个类。super() 方法用于调用父类的构造函数。
class PrinmaryStudent extends Student{
// 重写构造方法
constructor(name,grade){
//通过super(name)来调用父类的构造函数,否则父类的name属性无法正常初始化
super(name);
this.grade = grade;
}
// PrimaryStudent已经自动获得了父类Student的hello方法,我们又在子类中定义了新的myGrade方法
myGrade(){
alert('I am at grade' + this.grade);
}
}
(3)使用get和set获取或者改变属性
class Runoob{
constructor(name){
//在属性名称前使用下划线字符 _ 将 getter/setter 与实际属性分开
this._sitename = name;
}
// 获取属性的值
get sitname(){
return this._sitname;
}
// 改变属性的值
set sitname(x){
this._sitname = x;
}
}
let noob = new Runoob("菜鸟");
// 获取值,即使 getter 是一个方法,当你想获取属性值时也不要使用括号。
document.getElementById("demo").innerHTML = noob.sitename;
// 改变值
noob.sitname = "RUNOOB";
document.getElementById("demo").innerHTML = noob.sitename;
三、变量和常量
a. 变量 – var
1、声明
定义对象是用var关键字,而且变量本身类型不固定,属于动态语言(可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用var申明一次。)
和其他语言的不同,静态语言如果赋值类型不匹配,就会报错。
// 不指定变量类型
var a = 123;
a = 'ABC';
// 非严格模式下给未声明变量赋值创建的全局变量,是全局对象window的可配置属性,可以删除。
carname="Volvo"; // 将声明 window 的一个属性 carname。
2、解构赋值
利用简单的做法直接对多个变量 / 数组进行同时赋值,而不是一个一个赋值。
(1) 单纯赋值
// 对x,y,z三个变量同时赋值,赋值的时候,可以忽略一些元素。
var [x,y,z] = ['hello','javascript','es6']
var [,,z] = ['hello','javascript','es6']
// 对数组进行赋值
let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
// 从person对象当中快速取出若干属性name,age,passport
var {name,age,passport} = person;
(2) 使用默认值 =
// 如果person对象没有single属性,默认赋值为true:
var {name, single=true} = person;
(3) 将属性赋值给其他变量 :
let {name, passport:id} = person;
name; // '小明'
id; // 'G-12345678'
passport; // Uncaught ReferenceError: passport is not defined
// 注意: passport不是变量,而是为了让变量id获得passport属性:
(4) 快速交换值,不需要临时变量
[x,y] = [y,x]
b.常量 – const
类似C语言当中的define, 定义一些常量。
'use strict';
const PI = 3.14;
PI = 3; // 某些浏览器不报错,但是无效果!
PI; // 3.14
c、变量的作用域
(1)局部作用域
-
在函数体内部申明的变量作用域是整个函数体
-
当函数嵌套时,内部函数可以访问外部函数定义的变量,反过来则不行
-
如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。
注意:JavaScript的变量作用域实际上是函数内部,我们在**
for循环等语句块中是无法定义具有局部作用域的变量的**。通常使用let关键字。
let关键字 – 块级作用域
function foo(){
for (let i=0; i<100; i++){
}
}
(2)变量提升 – 变量书写规则
JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部。即所有的变量在函数顶部默认先定义,然后再相应的位置进行赋值。
声明 – var x;不是指初始化var x = 5; JavaScript 只有声明的变量会提升,初始化的不会。
// 在函数内部定义变量时,遵守“在函数内部首先申明所有变量”这一规则。最常见的做法是用一个var申明函数内部用到的所有变量
function foo(){
var
x = 1,
y = x + 1,
z,i;
// 其他语句
}
(3)全局作用域
全局变量
// 如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量。
function myFunction() {
carName = "Volvo";
}
// 函数体外
var carName = " Volvo";
function myFunction() {
}
window全局对象
JavaScript默认有一个全局对象window(浏览器窗口),即所有的全局作用域的变量都绑定到这个对象。无论是拥有全局作用域的变量,还是函数。
var course;
alert(window.course); // alert(course);
function foo(){
}
window.foo(); // foo()
通常的做法是把自己的所有变量和函数全部绑定到一个全局变量中,把自己的代码全部放入唯一的名字空间MYAPP中,会大大减少全局变量冲突的可能
// 唯一的全局变量MYAPP:
var MYAPP = {};
// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函数:
MYAPP.foo = function () {
return 'foo';
};
四、数组
1、定义
- 包含任意的数据类型。 和其他语言的不同,可以改变数组的长度。
- 数组也是一个对象,他的属性是每个元素的索引。
2、长度的计算以及改变
var arr = [1,2,3];
// 计算长度
arr.length;
// 通过赋值改变长度
arr.length = 6;
3、其他操作
// 通过值来确定元素的索引
arr.indexOf(1);
// 截取指定位置的元素,类似python中的切片.起止参数包括开始索引,不包括结束索引
arr.slice(0,3);
// 增加
// 每次在尾部插入
arr.push(5);
// 每次在头部插入
arr.unshift(5);
// 删除
// 每次在尾部删除
arr.pop(3);
// 每次在头部删除
arr.shift(3);
// 添加和删除任意位置的元素
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']
4、排序和反转
// 排序
arr.sort();
// 反转
arr.reverse();
5、连接
// 用指定的字符串拼接元素
var arr = ['A', 'B', 'C', 1, 2, 3];
arr.join('-'); // 'A-B-C-1-2-3'
// 拼接两个数组
var arr = ['A', 'B', 'C'];
arr.concat(1, 2, [3, 4]); // ['A', 'B', 'C', 1, 2, 3, 4]
五、函数
1、函数的定义
(1)一般函数
// 第一种 funtion 函数名(参数).末尾没有;
function abs(x){
if (x>0){
return x;
}
else{
return -x;
}
}
(2)匿名函数
// 第二种:匿名函数,没有函数名称,以分号结尾。函数表达式存储在变量当中。
var abs = function(x){
……
};
// 此时变量abs也可以当作函数使用
var result = abs(5);
(3)自调用函数 – 匿名自我调用的函数
(function(){
var x = "hello"; // 自己调用自己
})();
(4)Arrow Function(箭头函数)
// 参数 => 函数操作
function(x){
return x * x;
}
// 第一种:只包含一个表达式,连{ ... }和return都省略掉了
x => x * x
// 第二种:包含多条语句,这时候就不能省略{ ... }和return
x => {
if (x>0){
return x * x;
}
else{
return -x * x;
}
}
// 多参数
(x, y) => x * x + y * y
// 返回一个对象
x => ({foo: x})
箭头函数修复了this的指向
this总是指向词法作用域,也就是外层调用者obj。箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的。
// 原来的写法
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = function () {
return new Date().getFullYear() - this.birth; // this指向window或undefined
};
return fn();
}
};
// 用箭头函数修复this指向
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
// 箭头函数, 不需要var that = this;
// call()或者apply()调用箭头函数时,无法对this进行绑定,即传入的第一个参数被忽略.
var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
return fn();
}
};
obj.getAge(;)
(5)内嵌函数
// 函数可以嵌套
function foo(){
var x=1;
// 函数嵌套,内部函数可以访问外部函数定义的变量,反过来则不行
function bar(){
var y = x + 1;
}
}
闭包
-
闭包是指有权访问另一个函数作用域中变量的函数。创建闭包最常见的方式就是,在一个函数内部创建另一个函数。
-
使用场景:
1、读取函数内部的局部变量
2、让这些变量的值始终保存在内存中。
// 函数外不能读取函数内的变量
function f1(){
// 局部变量
var n=999;
}
alert(n); //error
// f2函数就是闭包
function f1(){
var n=999;
function f2(){
alter(n);
}
return f2;
}
var result = f1(); // result就是f2
result(); //999
// f1() 函数执行后,正常情况下 f1() 的整个内部作用域被销毁,占用的内存被回收。但是现在的 f1的内部作用域 f2() 还在使用,所以不会对其进行回收。f2() 依然持有对该作用域的引用,这个引用就叫做闭包。这个函数在定义的词法作用域以外的地方被调用。闭包使得函数可以继续访问定义时的词法作用域。
注意:
调用时的传入参数可以和定义的参数不一致,多或者少都可以。多的时候按照顺序传入,少的时候返回NaN.但是如果想得到传入的所有参数,就得用arugments关键字。
2、arguments关键字
只在函数的内部起作用,并且永远指向当前函数的调用者传入的参数。**并且即使函数没有定义任何的参数,还是可以拿到参数的值。**类似一个Array,但不是数组。
// 函数不定义任何参数
function abs(){
if (arguments.length == 0 ){
return 0;
}
# 即便没有定义参数,也可以拿到函数的值。
var x = arguments[0];
return x > 0 ? x : -x;
}
// 传入参数
abs(10);
注意:
如果想得到除了定义的参数的剩余的传入的参数,就可以使用rest参数。这是ES6标准。
3、rest参数
function foo(a,b,……,rest){
console.log('a = ' + a)
console.log('b = ' + b)
# 放在最后,拿到剩余的参数
console.log(rest)
}
4、return语句
注意:return后面的语句换行的时候,用{}包起来,书写规范。
return {
name:'foo'
};
5、高级函数
这是针对数组Array的高阶函数。高阶函数里面传入的是自定义的函数,对数组Array进行相应的操作。传入的函数都需要返回值,forEach()除外。
(1) map()/reduce()
这是Array的内置函数。 map用来将函数的功能作用在数组上,reduce用来进行累计操作。
// arr.map(函数名) -- 在数组上进行相关的操作,这个操作是自定义的函数或者是数据结构
function pow(x){
return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(pow);
// arr.map(String);
// arr.reduce(函数名) -- 函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算
arr.reduce(function (x, y) {
return x + y;
});
(2) filter
- 这是Array的内置函数。这是一个筛选函数,
filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。 filter()接收的回调函数,其实可以有多个参数。通常我们仅使用第一个参数element,表示Array的某个元素。回调函数还可以接收另外两个参数,表示元素的位置和数组本身
// 筛选数组中的奇数偶数
var r = arr.filter(function(x){
// 继续判断,返回的是布尔型数据.True的时候,返回。
return x % 2 !== 0;
});
利用filter,可以巧妙地去除Array的重复元素:
var r,
arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];
// indexOf总是返回第一个元素的位置,后续的重复元素位置与indexOf返回的位置不相等,因此被filter滤掉了。
// 回调函数
r = arr.filter(function(element,index.self){
return self.indexOf(element) === index;
})
console.log(r.toString());
(3) sort
直接利用sort()对数字进行排序,会导致与意愿违背的结果。所以需要在自定义比较方法,传入到高级函数sort()中。
// 比较数字
arr.sort(function(x,y){
if (x < y){
return -1;
}
if (x > y){
return 1;
}
return 0;
});
// 比较字母的时候,排序应该忽略大小写,按照字母序排序。就需要忽略大小写,即把所有的都转换成大写或者小写。
x1 = s1.toUpperCase();
x2 = s2.toUpperCase();
(4) 其他函数
-
every()方法可以判断数组的所有元素是否满足测试条件。 -
find()方法用于查找符合条件的第一个元素,如果找到了,返回这个元素,否则,返回undefined -
findIndex()和find()类似,也是查找符合条件的第一个元素,不同之处在于findIndex()会返回这个元素的索引,如果没有找到,返回-1
-
forEach()和map()类似,它也把每个元素依次作用于传入的函数,但不会返回新的数组。forEach()常用于遍历数组,因此,传入的函数不需要返回值。// 遍历数组 依次打印每个元素 arr.forEach(cconsole.log);
6、generator
传统函数以return结束,只能返回一次。generator和函数不同的是,generator由function定义(注意多出的号),并且,除了return语句,还可以用yield返回多次.
// 定义
function* foo(x){
yield x+1;
return x+2;
}
// 调用
foo(3) // 仅仅是创建了一个generator对象,还没有去执行它
// 第一种,不断地调用generator对象的next()方法。next()方法会执行generator的代码,然后,每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束了。如果done为true,则value就是return的返回值。
// 调用的时候,会依次传参数0,1,2。然后每个参数u执行一次,得到结果
var f = foo(3);
f.next(); // {value: 1, done: false}
// 第二种方法,用for ... of循环迭代generator对象
for (var x of foo(3)){
console.log(x);
}
六、字符串
1、单引号和双引号的使用
- 如果
'本身也是一个字符,那就可以用""括起来,比如"I'm OK"包含的字符是I,',m,空格,O,K这6个字符。 - 如果字符串内部既包含
'又包含"怎么办?可以用转义字符\来标识
2、多行字符串的表示 – 反引号
`这是一个
多行
字符串`;
七、条件判断和循环
1、条件判断
和C语言的用法大致相同,注意else的{}不要省略。省略的话,如果else下面有两条语句,那么只会影响一条。
var age = 20;
if (age >= 18)
alert('adult');
else
console.log('age < 18'); // 添加一行日志
alert('teenager'); // <- 这行语句已经不在else的控制范围了
2、循环
(1)for循环
// 和C语言一样,用来遍历数组
var arr = ['Apple', 'Google', 'Microsoft'];
var i, x;
for (i=0; i<arr.length; i++) {
x = arr[i];
console.log(x);
}
// for …… in 用来将对象的所有属性依次打印出来. -- 用来遍历对象
var o = {
name: 'Jack',
age: 20,
city: 'Beijing'
};
for (var key in o) {
console.log(key); // 'name', 'age', 'city'
}
// 数组也是对象,也可以用这种方式表示
for (var i in arr){
console.log(i);
console.log(arr[i]);
}
(2)while循环
和C语言一样
八、map和set – ES6标准新增的数据类型
JavaScript的默认对象表示方式{}可以视为其他语言中的Map或Dictionary的数据结构,即一组键值对。但是对象的键必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。为了解决这个问题,最新的ES6规范引入了新的数据类型Map。
1、Map
map是由键值对组成的。类似字典。
// 初始化空的Map 或者 有键值对的Map --里面是数组的形式
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
var m = new Map();
// 添加
m.set('Adam', 67);
// 存在
m.has('Adam');
// 删除
m.delete('Adam');
//取值
m.get('Adam');
2、Set
类似python里面的集合,也是一组key的集合,但不存储value。没有重复的key。
// 初始化空的 或者 有键值对的Set
var s = new Set();
var s = new Set([1, 2, 3]);
// 添加
s.add(4);
// 删除
s.delete(4);
九、iterable – ES6
1、Array Set Map都属于iterable,可以通过for …… of进行同一遍历。数组可以通过下标进行遍历,但是遍历Map和Set就无法使用下标。
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]);
}
2、for ... of循环和for ... in循环有何区别?
- for … in循环将把添加了额外的属性包括在内,但Array的length属性却不包括在内。
- for … of循环则完全修复了这些问题,它只循环集合本身的元素
3、forEach()方法,它接收一个函数,每次迭代就自动回调该函数。函数调用不要求参数必须一致.
// 数组
var a = ['A', 'B', 'C'];
a.forEach(function(element,index,array)){
console.log(elemet + ', index=' + index)
});
// A, index = 0
// B, index = 1
// C, index = 2
//Set
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) {
console.log(element);
});
//Map
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
console.log(value);
});
十、注意的点
1、运算符 == 和 ===
== : 表达式的值和类型满足一个即可
===:表达式的值和类型需要同时满足。一般switch中用恒等运算符
2、加法和连接都用+
var x = 10;
var y = 5;
var z = x + y; // z 的结果为 15
var x = 10;
var y = "5";
var z = x + y; // z 的结果为 "105"
3、浮点型数据使用注意事项
var x = 0.1;
var y = 0.2;
// var z = x + y // z 的结果为 0.30000000000000004
var z = (x * 10 + y * 10) / 10; // z 的结果为 0.3
4、字符串换行
var x = "Hello \
World";
5、Undefined 不是 Null
在 JavaScript 中, null 用于对象, undefined 用于变量,属性和方法。对象只有被定义才有可能为 null,否则为 undefined。
6、href="#"与href="javascript:void(0)"的区别
# 包含了一个位置信息,默认的锚是**#top** 也就是网页的上端。也可以使用这个去定位页面中的位置。当用户链接时,void(0) 计算为 0,但 Javascript 上没有任何效果。
<a href="javascript:void(0);">这是一个死链接</a>
<a href="#pos">可以到达指定的位置</a>
额外的属性包括在内,但Array的length属性却不包括在内。
- for … of循环则完全修复了这些问题,它只循环集合本身的元素
3、forEach()方法,它接收一个函数,每次迭代就自动回调该函数。函数调用不要求参数必须一致.
// 数组
var a = ['A', 'B', 'C'];
a.forEach(function(element,index,array)){
console.log(elemet + ', index=' + index)
});
// A, index = 0
// B, index = 1
// C, index = 2
//Set
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) {
console.log(element);
});
//Map
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
console.log(value);
});
十、注意的点
1、运算符 == 和 ===
== : 表达式的值和类型满足一个即可
===:表达式的值和类型需要同时满足。一般switch中用恒等运算符
2、加法和连接都用+
var x = 10;
var y = 5;
var z = x + y; // z 的结果为 15
var x = 10;
var y = "5";
var z = x + y; // z 的结果为 "105"
3、浮点型数据使用注意事项
var x = 0.1;
var y = 0.2;
// var z = x + y // z 的结果为 0.30000000000000004
var z = (x * 10 + y * 10) / 10; // z 的结果为 0.3
4、字符串换行
var x = "Hello \
World";
5、Undefined 不是 Null
在 JavaScript 中, null 用于对象, undefined 用于变量,属性和方法。对象只有被定义才有可能为 null,否则为 undefined。
6、href="#"与href="javascript:void(0)"的区别
# 包含了一个位置信息,默认的锚是**#top** 也就是网页的上端。也可以使用这个去定位页面中的位置。当用户链接时,void(0) 计算为 0,但 Javascript 上没有任何效果。
<a href="javascript:void(0);">这是一个死链接</a>
<a href="#pos">可以到达指定的位置</a>