JavaScript 语法笔记
目录
1.JS放在哪
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 浏览器不支持或禁用javascript时该元素显示-->
<noscript>
<h1 >"请支持javascript功能"</h1>;
</noscript>
<!-- type one action when click -->
<a href="javascript:alert('hello world!')">hello world!</a>
<!-- type two -->
<script type="text/javascript">
alert("hello world!");
</script>
<!-- type three defer:推迟脚本执行 async:启动新线程,异步执行脚本 -->
<!-- 两种属性只可用于此种外部脚本文件模式-->
<script src="first.js" type="text/javascript" defer async></script>
</body>
</html>
2.定义变量
javascript是弱类型语言,可以不定义变量直接而使用,存在显示定义变量(使用关键字var)和隐式定义变量两种,变量的数据类型也可随意转变。
<script type="text/javascript">
var a = true;
b = 2;
b = false;
</script>
3.基本数据类型
3.1 5种基本数据类型
- 数值类型
<script type="text/javascript">
//科学计数法
var a = 3E2; //300
var b = 2.1e-2; //0.021
//整数部分只有0时,可省略
var c = 0.3;
var d = .3; //0.3
//非十进制计数
var e = 0o14; //八进制
var f = 0x10; //十六进制
</script>
- 布尔类型
true 或 false
- 字符串类型
必须用引号括起来,可为单引号,也可为双引号
- undefined类型
用于表示某个变量或属性不存在,或已创建但未为其分匹配值
- null类型
用于表示变量有值,且为null
3.2 变量作用域
全局变量:函数外定义的 + 函数内不用var定义的 变量
局部变量:函数内用var定义的变量
- 局部变量的作用域仅限于该函数,javascript变量不存在块范围;
- 当变量同名时,在局部变量作用域内,局部变量会覆盖全局变量
3.3 变量提升
变量提升机制,指的是变量声明总是被解释器“提升”到函数体的顶部。
- 只是提升局部变量
- 只是提升声明部分,不包括赋值部分
- 只要有声明变量的语句都会被提升,不论该语句是否执行或有没有机会执行
<script type="text/javascript">
var a = "全局变量";
function f(){
alert(a); //undefined
var a = "局部变量";
alert(a); //局部变量
}
f();
</script>
所以以上代码效果等同于如下
<script type="text/javascript">
var a = "全局变量";
function f(){
var a; //变量声明提升
alert(a); //undefined
a = "局部变量";
alert(a); //局部变量
}
f();
</script>
3.4 let变量
- let 变量存在块作用域
- let变量不会自动添加为全局window对象的属性
- let变量不会提前装载
<script type="text/javascript">
var a = "全局变量";
function f(){
// 局部变量a覆盖了全局变量a,故全局变量无效
// 由于let变量不会提前装载,故此处的a为未定义的变量
alert(a); //语法错误
let a = "局部变量";
alert(a); //局部变量
}
f();
</script>
3.5 const定义变量
- 必须在定义时指定初始值,且以后不能改变值
4. 复合类型
javascript复合类型大致有以下三种:
- Object:对象
- Array:数组
- Function:方法
4.1 数组
- 数组长度可变
- 同一个数组的元素类型可互不相同
- 访问数组元素时不存在越界,访问未赋值的数组元素时,该元素值为undefined
// 创建数组的三种方式
var a = new Array();
var b = [];
var c = [1,3,true];
- 数组作为栈使用
push(e):元素入栈,返回入栈后数组长度
pop():元素出栈,返回出栈元素
入栈还是出栈,都是在数组的下标大的地方操作
- 数组作为队列使用
unshift(e):元素如队列,返回如队列后数组长度
shift():元素出队列,返回出队列元素
入队列还是出队列,都是在数组下标小(0)的地方操作
4.2 函数
for in循环
- 当遍历数组时,循环计数器是数组元素的索引值
- 当遍历对象时,循环计数器是对象的属性名
4.2.1 函数的定义
- 返回值不需要数据类型声明,形参也不需要数据类型声明
//命名函数
function f1(param-list){
statements
}
//匿名函数
[var f2 = ] function(param-list){
statements
};
[var f3 = ] new Function('param1','param2', ... , "statements");
4.2.2 函数、类、对象、方法、变量
函数是javascript中的“一等公民”,定义一个函数后,就得到了一个与函数名相同的类,也得到了一个Function类实例对象,它通常会附加给某个对象(默认window),作为其方法,最后,它也是一个变量。
<script type="text/javascript">
var person = function(name, age){
this.name = name;
this.age = age;
this.talk = function(){
document.write("Hi,I`m "+name+"<br />");
}
var walk = function(){
document.write("I am "+age+",I can walk..."+"<br />");
}
walk();
}
// person是Function的实例对象
alert(typeof person);
//person是一个方法,附属于window
person("Tom",18);
// person是一个类
var a = new person("Jerry",16);
a.talk();
//person是全局变量,此处被重新赋值为字符串
person = "no function";
alert(typeof person);
</script>
4.2.3 函数的实例属性和类属性
函数中的变量分为三种:
- 局部变量:在函数中用var声明的变量
- 实例属性:在函数中以this前缀修饰的变量
- 类属性:在函数中以函数名前缀修饰的变量
4.2.4 函数调用
1. 直接调用函数
调用者:函数附加的对象
调用者.函数(参数...)
2. 以call()方法调用
函数.call(调用者, 参数...)
<script type="text/javascript">
// 定义一个each函数
var each = function(array , fn)
{
for(var index in array)
{
// 以window为调用者来调用fn函数,
// index、array[index]是传给fn函数的参数
fn.call(null, index , array[index]);
}
}
// 调用each函数,第一个参数是数组,第二个参数是函数
each([4, 20 , 3] , function(index , ele)
{
alert(this);
document.write("第" + index + "个元素是:" + ele + "<br />");
});
</script>
call和apply的第一个参数是null/undefined时函数内的的this指向window或global
3.以apply()方法调用
函数.apply(调用者, [参数1, 参数2,... ])
4.2.5 函数的独立性
虽然定义函数时,可以将函数定义为某个类、某个对象的方法(助于引用时标识该方法,个人理解),但函数是独立的,不属于其他的类或对象。
- 样例一
<script type="text/javascript">
function Person(name)
{
this.name = name;
this.info = function()
{
console.log("我的name是:" + this.name);
}
}
var p = new Person("Tom");
// 调用p对象的info方法
p.info();
var name = "win";
// 以window对象作为调用者来调用p对象的info方法
p.info.call(window);
</script>
运行结果
函数的调用者,只是指明了函数运行的环境(this),p.info() 调用者是p对象,其name属性值为Tom;p.info.call(window) 调用者是window,其name属性值为win
- 样例二
<script type="text/javascript">
// 定义Dog函数,等同于定义了Dog类
function Dog(name , age , bark)
{
// 将name、age、bark形参赋值给name、age、bark实例属性
this.name = name;
this.age = age;
this.bark = bark;
// 使用内嵌函数为Dog实例定义方法
this.info = function()
{
return this.name + "的年纪为:" + this.age
+ ",它的叫声:" + this.bark;
}
}
// 创建Dog的实例
var dog = new Dog("旺财" , 3 , '汪汪,汪汪...');
// 创建Cat函数,对应Cat类
function Cat(name,age)
{
this.name = name;
this.age = age;
}
// 创建Cat实例。
var cat = new Cat("kitty" , 2);
// 将dog实例的info方法分离出来,再通过call方法完调用info方法,
// 此时以cat为调用者
console.log(dog.info.call(cat));
</script>
运行结果
4.2.6 函数提升
函数提升的前提是该函数采用命名函数的形式定义函数
- 样例一(提升)
<script type="text/javascript">
// 调用add函数
console.log(add(2, 5));
// 定义add函数(会发生函数提升)
function add(a , b)
{
console.log("执行add函数");
return a + b;
}
</script>
相当于
<script type="text/javascript">
// 定义add函数
function add(a , b)
{
console.log("执行add函数");
return a + b;
}
// 调用add函数
console.log(add(2, 5));
</script>
- 样例二(不提升)
<script type="text/javascript">
// 调用add函数
console.log(add(2, 5)); //error
// 定义add函数,此时只提升add变量名,函数定义不会被提升
var add = function(a , b)
{
console.log("执行add函数");
return a + b;
}
</script>
相当于
<script type="text/javascript">
var add;
// 调用add函数
console.log(add(2, 5));
// 定义add函数,此时只提升add变量名,函数定义不会被提升
add = function(a , b)
{
console.log("执行add函数");
return a + b;
}
</script>
- 样例三(提升)
<script type="text/javascript">
function test(){
// 调用add函数
console.log(add(2, 5));
// 定义add函数(会发生函数提升)
function add(a , b)
{
console.log("执行add函数");
return a + b;
}
}
test();
</script>
相当于
<script type="text/javascript">
function test(){
function add(a , b)
{
console.log("执行add函数");
return a + b;
}
// 调用add函数
console.log(add(2, 5));
}
test();
</script>
- 样例四(不提升)
<script type="text/javascript">
function test(){
// 调用add函数
console.log(add(2, 5)); //error
// 定义add函数,此时只提升add变量名,函数定义不会被提升
var add = function(a , b)
{
console.log("执行add函数");
return a + b;
}
}
test();
</script>
相当于
<script type="text/javascript">
function test(){
var add;
// 调用add函数
console.log(add(2, 5)); //error
// 定义add函数,此时只提升add变量名,函数定义不会被提升
add = function(a , b)
{
console.log("执行add函数");
return a + b;
}
}
test();
</script>
4.2.7 变量与函数同名
定义变量时赋值,不论在同名函数前还是后,都会覆盖同名函数;定义变量时不赋值,不论在同名函数前还是后,同名函数都会覆盖变量
<script type="text/javascript">
function a(){
}
var a; // 定义变量,不指定初始值
console.log(a);// 输出a的函数体
var b; // 定义变量,不指定初始值
function b(){
}
console.log(b);// 输出b的函数体
var c = 1; // 定义变量,并指定初始值
function c(){
}
console.log(c);// 输出1
function d(){
}
var d = 1; // 定义变量,并指定初始值
console.log(d);// 输出1
</script>
4.2.8 箭头函数
箭头函数相当于Lambda表达式,形如 (param1, param2, ... ) => { statements }
- 只有一个形参时,可省略括号
- 没有形参时,不可省略括号
- 只有一条语句时,可省略花括号
- 只有一条语句,且是return语句时,可省略return
- 箭头函数不绑定arguments,所以箭头函数的arguments总是引用当前上下文的arguments
<script type="text/javascript">
//var arguments = "Tom";
// 箭头函数中arguments引用当前上下的arguments,即"Tom"字符串
var arr = () => arguments;
console.log(arr()); // 输出Tom
function foo()
{
// 箭头函数中arguments引用当前上下的arguments,
// 此时arguments代表调用foo函数的参数
var f = (i) => 'Hello,' + arguments[0];
return f(2);
}
console.log(foo("Tom", "Jerry")); // 输出Hello,Tom
</script>
对于普通函数
如果直接调用普通函数,函数中的this代表全局对象(window);如果通过new调用函数创建对象,那么函数中的this代表所创建的对象。
<script type="text/javascript">
var f = function(){
this.age = 100;
console.log(this + this.age);
window.age = 90;
console.log(this + this.age);
}
f(); //直接调用函数f,则函数f中的this代表window
new f(); //通过new创建对象使函数f得到调用,此时函数f中的this代表f对象
</script>
再看一个例子
<script type="text/javascript">
function Person() {
// Person()作为构造器使用时,this代表该构造器创建的对象
this.age = 0;
setInterval(function growUp(){
// 对于普通函数来说,直接执行该函数时,this代表全局对象(window)
// 因此下面的this不同于Person构造器中的this
console.log(this === window);
this.age++;
}, 1000);
}
var p = new Person();
setInterval(function(){
console.log(p.age); // 此处访问p对象的age,将总是输出0
}, 1000);
</script>
此处growUp方法里的this是始终为window,区别于以下箭头函数时。
对于箭头函数
箭头函数中的this总是代表包含箭头函数的上下文
<script type="text/javascript">
function Person() {
// Person()作为构造器使用时,this代表该构造器创建的对象
this.age = 0;
setInterval(() => {
// 箭头函数中this总代表包含箭头函数的上下文。
console.log(this === window);
// 此处的this,将完全等同于Person构造器中的this
this.age++;
}, 1000);
}
var p = new Person();
setInterval(function(){
console.log(p.age); // 此处访问p对象的age,将总是输出数值不断加1
}, 1000);
</script>
值得注意的是,此处箭头函数里的this等同于Person构造器里的this,此处Person构造器里的this代表Person对象,故箭头函数里的this为Person对象
<script type="text/javascript">
function Person() {
// Person()作为构造器使用时,this代表该构造器创建的对象
this.age = 0;
setInterval(() => {
// 箭头函数中this总代表包含箭头函数的上下文。
console.log(this === window); //true
// 此处的this,将完全等同于Person构造器中的this
this.age++;
}, 1000);
}
Person();
setInterval(function(){
console.log(window.age); // 此处访问window对象的age,将总是输出数值不断加1
}, 1000);
</script>
此时直接调用Person函数,导致Person函数里的this代表window,也就是箭头函数的上下文是window,所以箭头函数的this就是window
5. 面向对象
javascript函数定义不支持继承语法,没有完善的继承机制,因此习惯上称Javascript是基于对象的脚本语言。所有的类是Object类的子类外,没有其他父子继承关系。
5.1 prototype实现继承
javascript的prototype属性代表了该类的原型对象,其是一Object对象,将javascript的prototye属性设为父类实例,可实现javascript语言的继承。
<script type="text/javascript">
// 定义一个Person类
function Person(name, age)
{
this.name = name;
this.age = age;
}
// 使用prototype为Person类添加sayHello方法
Person.prototype.sayHello = function()
{
console.log(this.name + "向您打招呼!");
}
var per = new Person("牛魔王", 22);
per.sayHello(); // 输出:牛魔王向您打招呼!
// 定义一个Student类
function Student(grade){
this.grade = grade;
}
// 将Student的prototype设为Person对象
Student.prototype = new Person("未命名" , 0);
// 使用prototype为Student类添加intro方法
Student.prototype.intro = function(){
console.log("%s是个学生,读%d年级" , this.name, this.grade);
}
var stu = new Student(5);
stu.name = "孙悟空";
console.log(stu instanceof Student); // 输出true
console.log(stu instanceof Person); // 输出true
stu.sayHello(); // 输出:孙悟空向您打招呼!
stu.intro(); //输出:孙悟空是个学生,读5年级
</script>
Student的原型是Person对象,相当于设置Student继承了Person,这样Student类将会得到Person类的属性和方法。Student对象既可调用Person的实例方法,也可调用Student的实例方法。
5.2 构造器实现伪继承
<script type="text/javascript">
// 定义一个Person类
function Person(name, age)
{
this.name = name;
this.age = age;
// 为Person类定义sayHello方法
this.sayHello = function()
{
console.log(this.name + "向您打招呼!");
}
}
var per = new Person("牛魔王", 22);
per.sayHello(); // 输出:牛魔王向您打招呼!
// 定义Student类
function Student(name, age, grade)
{
// 定义一个实例属性引用Person类
this.inherit_temp = Person;
// 调用Person类的构造器
this.inherit_temp(name, age);
this.grade = grade;
}
// 使用prototype为Student类添加intro方法
Student.prototype.intro = function(){
console.log("%s是个学生,读%d年级" , this.name, this.grade);
}
var stu = new Student("孙悟空", 34, 5);
console.log(stu instanceof Student); // 输出true
console.log(stu instanceof Person); // 伪继承,所以输出false
stu.sayHello(); // 输出:孙悟空向您打招呼!
stu.intro(); //输出:孙悟空是个学生,读5年级
</script>
第21行,以this为调用者,调用了Person的构造器,这样Person构造器中的this就是Student。Student对象既可调用Person的实例方法,也可调用Student的实例方法。
5.3 使用apply或call实现伪继承
这种方式和5.2相似
<script type="text/javascript">
// 定义一个Person类
function Person(name, age)
{
this.name = name;
this.age = age;
// 为Person类定义sayHello方法
this.sayHello = function()
{
console.log(this.name + "向您打招呼!");
}
}
var per = new Person("牛魔王", 22);
per.sayHello(); // 输出:牛魔王向您打招呼!
// 定义Student类
function Student(name, age, grade)
{
Person.call(this, name, age);
// Person.apply(this, [name, age]);
this.grade = grade;
}
// 使用prototype为Student类添加intro方法
Student.prototype.intro = function(){
console.log("%s是个学生,读%d年级" , this.name, this.grade);
}
var stu = new Student("孙悟空", 34, 5);
console.log(stu instanceof Student); // 输出true
console.log(stu instanceof Person); // 伪继承,所以输出false
stu.sayHello(); // 输出:孙悟空向您打招呼!
stu.intro(); //输出:孙悟空是个学生,读5年级
</script>
6. 创建对象
- 使用new关键字调用构造器创建对象
var p = new Person();
- 使用Object类创建对象
var obj = new Object();
- 使用JSON语法创建对象
var person = {
name:"Tom",
age:20
}
上一篇: 数据库实验11 SQL语言-数据库完整性
下一篇: 第四章 循环结构程序设计