javascript高级
JavaScript 是什么
JavaScript,也称ECMAScript,是一种基于对象和事件驱动并具有相对安全性并广泛用于客户端网页开发的脚本语言,同时也是一种广泛用于客户端Web开发的脚本语言。
javascript的基本特点
是一种解释性脚本语言(代码不进行预编译)。
主要用来向 HTML 页面添加交互行为。
可以直接嵌入 HTML 页面,但写成单独的js文件有利于结构和行为的分离。
javascript的特性
JavaScript 的组成
组成部分 | 说明 |
---|---|
Ecmascript | 描述了该语言的语法和基本对象 |
DOM | 描述了处理网页内容的方法和接口 |
BOM | 描述了与浏览器进行交互的方法和接口 |
基本数据类型(值类型)
- String
- Number
- Boolean
- Null
- Undefined
复杂类型
- Object
- Array
- Date
- Function
类型检测
typeof
instanceof
Object.prototype.toString.call()
值类型和引用类型在内存中的存储方式
- 值类型按值存储
- 引用类型按引用存储
一、面向对象介绍
什么是对象
Everything is object (万物皆对象)
面向对象编程 —— Object Oriented Programming,简称 OOP ,是一种编程开发思想。
它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。
面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性。
面向对象的特性:
- 封装性
- 继承性
- [多态性](抽象)
编程思想:
面向过程:所有的事情都是亲力亲为,注重的是过程
面向对象:提出需求,找对象,对象解决,注重的是结果
js不是一门面向对象的语言,是基于对象的语言,js来模拟面向对象
二、创建对象方式
简单方式
我们可以直接通过 new Object() 创建:
var person = new Object()
person.name = 'Jack'
person.age = 18
person.sayName = function () {
console.log(this.name)
}
console.log(Object.name) //Jack
对象字面量
每次创建通过 new Object() 比较麻烦,所以可以通过它的简写形式对象字面量来创建:
var person = {
name: 'Jack',
age: 18,
sayName: function () {
console.log(this.name)
}
}
对于上面的写法固然没有问题,但是假如我们要生成两个 person 实例对象呢?
var person1 = {
name: 'Jack',
age: 18,
sayName: function () {
console.log(this.name)
}
}
var person2 = {
name: 'Mike',
age: 16,
sayName: function () {
console.log(this.name)
}
}
通过上面的代码我们不难看出,这样写的代码太过冗余,重复性太高。
简单方式的改进:工厂函数
我们可以写一个函数,解决代码重复问题:
function createPerson (name, age) {
return {
name: name,
age: age,
sayName: function () {
console.log(this.name)
}
}
}
//然后生成实例对象:
var p1 = createPerson('Jack', 18)
var p2 = createPerson('Mike', 18)
这样封装确实爽多了,通过工厂模式我们解决了创建多个相似对象代码冗余的问题,
但却没有解决对象识别的问题(即怎样知道一个对象的类型)
更优雅的工厂函数:构造函数
一种更优雅的工厂函数就是下面这样,构造函数:
function Person (name, age) {
this.name = name
this.age = age
this.sayName = function () {
console.log(this.name)
}
}
var p1 = new Person('Jack', 18)
p1.sayName() // => Jack
var p2 = new Person('Mike', 23)
p2.sayName() // => Mike
在该示例中,从表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。
那就是对于每一个实例对象,type 和 sayHello 都是一模一样的内容,
每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。
对于这种问题我们可以把需要共享的函数定义到构造函数外部:
function sayHello = function () {
console.log('hello ' + this.name)
}
function Person (name, age) {
this.name = name
this.age = age
this.type = 'human'
this.sayHello = sayHello
}
var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)
这样确实可以了,但是如果有多个需要共享的函数的话就会造成全局命名空间冲突的问题。
你肯定想到了可以把多个函数放到一个对象中用来避免全局命名空间冲突的问题:
var fns = {
sayHello: function () {
console.log('hello ' + this.name)
},
sayAge: function () {
console.log(this.age)
}
}
function Person (name, age) {
this.name = name
this.age = age
this.type = 'human'
this.sayHello = fns.sayHello
this.sayAge = fns.sayAge
}
var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)
至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。
但是代码看起来还是那么的格格不入,那有没有更好的方式呢?
原型对象
内容引导
- 使用 prototype 原型对象解决构造函数的问题
- 分析 构造函数、prototype 原型对象、实例对象 三者之间的关系
- 属性成员搜索原则:原型链
- 实例对象读写原型对象中的成员
- 原型对象的简写形式
- 原生对象的原型
- Object
- Array
- String
- …
- 原型对象的问题
- 构造的函数和原型对象使用建议
更好的解决方案: prototype
Javascript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象
这个对象的所有属性和方法,都会被构造函数的实例继承。
这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上
function Person (name, age) {
this.name = name
this.age = age
}
console.log(Person.prototype)
Person.prototype.type = 'human'
Person.prototype.sayName = function () {
console.log(this.name)
}
var p1 = new Person(...)
var p2 = new Person(...)
这时所有实例的 type 属性和 sayName() 方法,
其实都是同一个内存地址,指向 prototype 对象,因此就提高了运行效率。