0%

JavaScript|编程艺术

image-20210904233318993

早期的JavaScript在各个浏览器环境的兼容性很差,编程人员必须编写大量浏览器嗅探代码来探测浏览器,几个关键新技术给JavaScript带来了春天。

W3C (万维网联盟)推出的标准化DOM (Document Object Model,文档对象模型),它支持市场上所有常见的浏览器。其次,兴起的Ajax技术,(以DOM和JavaScript语言(以及CSS和XHTML)为基本要素,基于Ajax技术的网站离不开JavaScript和DOM脚本。

只要运用得当,再注意避开那些经典的JavaScript 陷阱,DOM编程技术就可以成为Web开发工具箱里又一件功能强大甚至是不可或缺的好东西。

JavaScript简史

Web标准三大件:XHTML (可扩展的超文本标记语言)和CSS (层叠样式表),DOM(文档对象模型)

JavaScript与DOM

在JavaScript出现之前,Web浏览器局限于枯燥的文本,在JavaScript出现之后,网页的交互性显著提高。

1.关于JavaScript

JavaScript是一种脚本语言,JavaScript脚本通常只能通过Web浏览器去完成某种操作而不是如普通意义上的程序那样可以独立运行。

2.关于DOM

DOM是对文档内容对象化的方法,有面对对象编程的思想。所谓对象就是人们约定俗成的客观事物和描述性的抽象概念,万物皆可对象

Web开发标准

1.DHTML

DHTML即动态HTML,DHTML = HTML + CSS + JavaScript

  • HTML负责标记网页各部分元素

  • CSS负责设置各个元素的排布

  • JavaScript负责实时操控和改变各有关样式

2.相关标准

DOM的实际用处相当于一种API(应用编程接口),即一组各方共同认可的标准,而不是一种编程语言。

相关语法会应所使用的编程语言而改变,而标准是保持不变的。你可以通过JavaScript来使用DOM,今后也可以通过PHP和Python来使用它

W3C对DOM的定义是:“一个与系统平台和编程语言无关的接口,程序和脚本可以通过这个接口动态地对文档的内容、结构和样式进行访问和修改。”

JavaScript语法

准备工作

1.js的添加方法

(1)第一种方法是把js代码直接放在html文档中,一般放在最后

(2)最佳的方案是将js代码单独放在一个文件中,用<script>标签的src属性指向该文件

1
<script type="text/javascript" src="file.js">
2.语言类型

程序设计语言有解释型和编译型两大类。

Java和c++等语言需要编译器才能转化为二进制的可执行文件

解释型语言如js,则不需要编译器,它们仅仅需要解释器

编译型语言再编译阶段就会报错,而解释型语言要到执行阶段才能查到错误

变量

1.基本内容

变量即会发生变化的值,把值存入变量的操作称为赋值。

值得注意的是js与python一样是无类型语言,它们都允许直接对变量进行赋值而无需声明,其他语言如c语言,java等都要求——先声明,再使用

但是为了编程的规范型,js也往往有对变量做出声明的习惯

1
2
3
var mood;
var age;
var mood, age;

在js中,变量和其他语法元素名字是区分大小写的

2.命名规则
  • JavaScript语法不允许变量的名字中包含空格或标点符号(但美元符号“$”例外)
  • JavaScript变量名允许包含字母、数字、美元符号和下划线字符。为了让比较长的变量名有更好的可读性,可以在变量名中的适当位置插入一个下划线字符
3.数据类型

js中无需进行类型声明,字符串型和数值型的赋值语法是一样的。

依据语言是否要对数据类型进行声明把语言分为强类型弱类型

例:

1
2
var age = "三";
age = 33;

其在强类型语言中是非法的,但在js语言中完全ok(好像并不是什么好事)

4.数组

字符串,数值和布尔值都属于离散值,它们任意时刻就只能有一个值。而数组则可以用一个变量同时存储一组值

(1)数组需要用关键字Array来声明

1
2
var beatles = Array(4); //确定数组长度
var bratles = Array(); //不确定长度

(2)向数组添加元素的操作称为填充

1
2
3
beatles[0] = "Jhon"; //通过下标确定存放位置
var beatles = Array("j","p","r");
var beatles = ["j","p","r"];

事实上,只需要一对方括号就可以创建我们想要的数组,但是在声明或填充数组时写出Array关键字是一个良好的编程习惯

(3)关联数组

数组的下标可以是字符串,通过各元素的名字来引用它们,提高了脚本的可读性

1
2
3
4
var lennon = Array();
lennon[ "name"] = "John";
lennon["year"] = 1940;
lennon["living"] = false;

再用新的数组来存储数组(二维数组的概念)

1
2
3
var beatles = Array();
beatles[0] = lennon;
//则有beatles[0]["year"]值为1940
5.操作

只说一点——js的操作符是真的骚,有很多奇奇怪怪的骚操作。

条件与循环语句

条件语句与其他语言是一样一样的,都涉及到比较操作符和逻辑操作符

循环语句中for循环最常用于对数组的全体元素进行遍历处理,常常会用到array.length属性

1
2
3
4
var beatles = Array("John" ,"Paul" ,"George" ,"Ringo");
for (var count = 0 ; count < beatles.length; count++){
alert(beatles[ count]);
}

函数

重复使用的一组语句,可以打包成为一个函数,供我们随时调用。作为种良好的编程习惯,你们应该先对函数做出定义再调用它们。

1.基本内容

(1)函数的声明

1
function shout(){}

(2)函数的调用

1
shout();
2.参数

(1)函数接收参数

1
2
3
4
function multiply(num1,num2) {
var total = num1 * num2;
alert(total);
}

我们可以在任意位置调用这个函数

1
multiply(10,2); 

(2)函数返回参数

函数不仅仅可以接收数据,还可以返回数据

1
2
3
4
5
6
7
8
9
10
function convertToCelsius(temp) {
var result = temp - 32;
result = result / 1.8;
return result;
}

var temp_fahrenheit = 95;
var temp_celsius = convertToCelsius(temp_ fahrenheit);
alert(temp_celsius);

函数真正的价值体现在,我们可以把它们当作数据类型来使用——把一个函数的调用结果赋值给一个变量

3.作用域

在函数内定义的变量是局部变量,在函数外的变量是全局变量。一定要有用var声明变量的习惯,否则会有二义性的隐患

对象

对象是一种非常重要的数据类型,对象里的数据可以通过:属性和方法两种方式访问

  • 属性即隶属于特定对象的变量
  • 方法是只有特定对象才能调用的函数

总而言之,对象是单个实物的抽象,对象是一个容器,封装了属性(property)和方法(method)

1.构造函数

所谓构造函数,就是专门用来生成实例对象的函数。它就是对象的模板,描述实例对象的基本结构。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。

js的对象体系是基于构造函数,而其他面向对象语言(c++和java)是基于类(class)的,对象就是类的实例

构造函数就是一个普通的函数,但是有自己的特征和用法。

实例:

1
2
3
var Vehicle = function () {
this.price = 1000;
};

Vehicle就是构造函数。为了与普通函数区别,构造函数名字的第一个字母通常大写,构造函数有两大特点:

  • 函数体内部使用了this关键字,代表了所要生成的对象实例
  • 生成对象的时候,必须使用new命令。
2.new命令

(1)基本用法

new命令的作用,就是执行构造函数,返回一个实例对象

调用对象中的属性和方法都用“点”语法来访问

例子1:

1
2
3
4
5
6
var Vehicle = function () {
this.price = 1000;
};

var v = new Vehicle(); //new可以执行构造函数
v.price // 1000
  • 代码通过new命令,让构造函数Vehicle生成一个实例对象,保存在变量v
  • 这个新生成的实例对象,从构造函数Vehicle得到了price属性(继承)
  • new命令执行时,构造函数内部的this,就代表了新生成的实例对象,this.price表示实例对象有一个price属性,值是1000(this指向生成的实例对象)

例子2带参数:

1
2
3
4
5
var Vehicle = function (p) {
this.price = p;
};

var v = new Vehicle(500);

构造函数本身就是一个普通函数,但是new命令让它焕然一新,没有new命令,this就代表了全局对象

(2)new命令原理

  • 创建一个空对象,作为将要返回的对象实例。
  • 将这个空对象的原型,指向构造函数的prototype属性。
  • 将这个空对象赋值给函数内部的this关键字。
  • 开始执行构造函数内部的代码。

构造函数内部,this指的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。构造函数之所以叫“构造函数”,就是说这个函数的目的,就是操作一个空对象(即this对象),将其“构造”为需要的样子。

1
2
3
4
5
6
7
var Vehicle = function () {
this.price = 1000;
return 1000;
};

(new Vehicle()) === 1000
// false

如果构造函数内部有return语句,而且return后面跟着一个对象,new命令会返回return语句指定的对象;否则,就会不管return语句,返回this对象。

但是,如果return语句返回的是一个跟this无关的新对象,new命令会返回这个新对象,而不是this对象。这一点需要特别引起注意。

1
2
3
4
5
6
7
var Vehicle = function (){
this.price = 1000;
return { price: 2000 };
};

(new Vehicle()).price
// 2000
3.内建对象

即js本身为用户写好的对象,比如我们所熟悉的数组对象

1
2
var beatles = new Array();
beatles.length;

length就是Array对象的一个写好的属性

除此之外,js常用的内建对象还有Math对象和Date对象

Math对象:主要用于数值得运算

1
2
3
var num = 7.561;
var num = Math.round(num);
alert(num);

上述例子,利用了Math对象得round方法,把十进制数舍入为一个整数

Date对象:用来存储和检索一个特定的日期和时间相关的信息。

1
2
var current_date = new Date();
var today = current_date.getDay();

上述例子可以快速判断给定日期是星期几。

4.宿主对象

宿主对象,不是js语言本身的,而是由它的运行环境提供的(一般来说都是web浏览器提供的),包括Form,Image,Element等,我们可以通过这些对象获取到给定网页上的对象元素信息。

宿主对象的功能其实于DOM中的document的功能是一样的,所有我们常用DOM而少用宿主对象。

5.prototype 对象

(1)构造函数的缺点

js是通过构造函数生成新对象,实例对象的属性和方法,可以定义在构造函数内部。每new一次,属性和方法都要重新创造一次

例子:

1
2
3
4
5
6
7
8
9
10
11
12
function Cat(name, color) {
this.name = name;
this.color = color;
this.meow = function () {
console.log('喵喵');
};
}

var cat1 = new Cat('大毛', '白色');
var cat2 = new Cat('二毛', '黑色');

cat1.meow === cat2.meow //false

上述代码中,两个对象都具有相同的meow方法,但是每新建一个实例,就会新建一个meow方法,这无疑浪费了资源。

(2)prototype 属性

为了解决构造函数的缺点,让所有实例对象共享“资源”,节省内存,我们就要用到prototype 属性

1
2
3
4
5
6
7
8
9
10
function Animal(name) {
this.name = name;
}
Animal.prototype.color = 'white';

var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');

cat1.color // 'white'
cat2.color // 'white'

对象cat1和cat2共享color属性,只要修改该属性内容,变动就立刻会体现在所有实例对象上

注意

实例对象cat1color属性改为black,就使得它不再去原型对象读取color属性

1
2
3
4
cat1.color = 'black';
cat1.color // 'black'
cat2.color // 'yellow'
Animal.prototype.color // 'yellow';
6.Object对象

js在Object对象上提供了很多方法用于处理面向对象编程的相关操作

(1)Object的读写操作

读取原型对象:getPrototypeOf

1
2
3
4
5
6
7
8
//获得构造函数原型
var F = function () {};
var f = new F();
Object.getPrototypeOf(f) === F.prototype // true

// 函数的原型是 Function.prototype
function f() {}
Object.getPrototypeOf(f) === Function.prototype // true

属性共享setPrototypeOf

Object.setPrototypeOf方法为参数对象设置原型,返回该参数对象。它接受两个参数,第一个是现有对象,第二个是原型对象。

1
2
3
4
5
6
var a = {};
var b = {x: 1};
Object.setPrototypeOf(a, b);

Object.getPrototypeOf(a) === b // true
a.x // 1

上面代码中,Object.setPrototypeOf方法将对象a的原型,设置为对象b,因此a可以共享b的属性。

(2)Object.create() 继承创建实例对象

构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.create()方法。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
var person1 = {
name: '张三',
age: 38,
greeting: function() {
console.log('Hi! I\'m ' + this.name + '.');
}
};

var person2 = Object.create(person1);

person2.name // 张三
person2.greeting() // Hi! I'm 张三.

对象person2继承了person1的属性和方法

(3)获取对象所有属性

1
2
3
4
Object.getOwnPropertyNames(Date)
// ["parse", "arguments", "UTC", "caller", "name", "prototype", "now", "length"]

Object.keys(Date) // []
  • Object.getOwnPropertyNames方法返回Date所有自身的属性名
  • Object.keys只获取那些可以遍历的属性

(4)遍历属性

获得对象的所有可遍历属性(不管是自身的还是继承的),可以使用for...in循环

1
2
3
4
5
6
7
8
9
10
11
var o1 = { p1: 123 };

var o2 = Object.create(o1, {
p2: { value: "abc", enumerable: true }
});

for (p in o2) {
console.info(p);
}
// p2
// p1
7.this关键字

(1)一般含义

在上述例子中,this可以用在构造函数中,表示实例对象。this总能返回一个对象,简单说,this就是属性或方法“当前”所在的对象。

例子:

1
2
3
4
5
6
7
8
9
var person = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};

person.describe()
// "姓名:张三"

this.name表示name属性所在的那个对象。由于this.name是在describe方法中调用,而describe方法所在的当前对象是person,因此this指向personthis.name就是person.name。

(2)this的动态指向

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function f() {
return '姓名:'+ this.name;
}

var A = {
name: '张三',
describe: f
};

var B = {
name: '李四',
describe: f
};

A.describe() // "姓名:张三"
B.describe() // "姓名:李四"

函数f内部使用了this关键字,随着f所在的对象不同,this的指向也不同。只要函数被赋给另一个变量,this的指向就会变

网页实例:

1
2
3
4
5
6
7
8
<input type="text" name="age" size=3 onChange="validate(this, 18, 99);">

<script>
function validate(obj, lowval, hival){
if ((obj.value < lowval) || (obj.value > hival))
console.log('Invalid Value!');
}
</script>

上面代码是一个文本输入框,每当用户输入一个值,就会调用onChange回调函数,验证这个值是否在指定范围。this不断指向用户输入的值。