JavaScript语言是面向对象编程的语言(Object Oriented Programming OOP)
(1)对象是单个实物的抽象
(2)对象是一个容器,封装了属性(property)和方法(method)
通常需要一个模板,表示某一类实例对象的共同特征,然后实例对象根据这个模板生成。这个模板就是构造函数
典型的面向对象编程语言(比如 C++ 和 Java),都有“类”(class)这个概念。所谓“类”就是实例对象的模板,实例对象就是“类”的实例。但是,JavaScript 语言的对象体系,不是基于“类”的,而是基于构造函数(constructor)和原型链(prototype)。
所以在JavaScript中,构造函数就相当于实例对象的类,实例对象就是构造函数的实例。
构造函数 = 类
实例对象 = 构造函数的实例
构造函数就是一个普通的函数,但具有独特特征和用法
不带参数的构造函数
var Vehicle = function () { this.price = 1000; }; var v = new Vehicle(); //创建实例对象 var v1 = new Vehicle; //new命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号。 console.info(v.price); console.info(v1.price);Vehicle就是构造函数,为了区分普通函数,构造函数名字的第一个字母为大写。
构造函数的特点有两个:
函数体内部使用this关键字,代表了所要生成的对象实例
生成实例对象的时候,必须使用new命令
2.带参数的构造函数
var Vehicle = function (p) {this.price = p
}
var v = new Vehicle(5000); //创建实例对象
console.info(v.price)
//不用new去调用构造函数
var Vehicle = function () {
this.price = 1000;
};var v3 = Vehicle(); //没有用new调用,所以这里为一个普通函数使用
console.info(v3); //v3由于未使用new调用构造函数,所以是未定义的变量,并不是一个实例对象
console.info(price); //由于直接调用构造函数,相当于把构造函数看作一个普通函数,所以price此时是一个全局变量
//为了避免将构造函数当作普通函数调用,设置报错提醒
var Fubar = function (bar) {'use strict'; //如果直接调用该构造函数,则会报错提醒没有使用new
this.bar = bar;
}//var f = Fubar(1111); //未使用new,所以报错
//构造函数的另一个写法
function Fubar (bar) {
'use strict';
this.bar = bar;
}
//var f1 = Fubar(2222); //未使用new,所以报错
var f2 = new Fubar(3333);
console.info(f2.bar);
console.info(new Fubar(4444).bar);
var Fur = function () {}
var f = new Fur();
//f的原型的对象Fur.prototype
console.info(Object.getPrototypeOf(f) === Fur.prototype)
//三种特殊的原型对象
console.info(Object.getPrototypeOf({}) === Object.prototype)
console.info(Object.getPrototypeOf(Object.prototype) === null)
function fun() {}
console.info(Object.getPrototypeOf(fun) === Function.prototype)
//new命令可以用setPrototypeOF方法模拟
var Stu = function () {
this.foo = 'bar';
}
var s = new Stu();
console.info(s.foo)
//等同于
var s1 = Object.setPrototypeOf({},Stu.prototype)
Stu.call(s1)
console.info(s1.foo)
// 上面代码中,`new`命令新建实例对象,其实可以分成两步。
// 第一步,将一个空对象的原型设为构造函数的`prototype`属性(上例是`F.prototype`);
// 第二步,将构造函数内部的`this`绑定这个空对象,然后执行构造函数,使得定义在`this`上面的方法和属性(上例是`this.foo`),都转移到这个空对象上。
//this指向的应用
var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
// 单独执行
f() // 1
// obj 环境执行
obj.f() // 2
// 上面代码中,函数`f`在全局环境执行,`this.x`指向全局环境的`x`;在`obj`环境执行,`this.x`指向`obj.x`。
面向对象编程很重要的一个方面,就是对象的继承。A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的。 传统上,JavaScript 语言的继承不通过 class,而是通过“原型对象”(prototype)实现
所有对象的原型最终都可以上溯到Object.prototype
,即Object
构造函数的prototype
属性。也就是说,所有对象都继承了Object.prototype
的属性。这就是所有对象都有valueOf
和toString
方法的原因,因为这是从Object.prototype
继承的。
constructor属性
prototype
对象有一个constructor
属性,默认指向prototype
对象所在的构造函数。
由于constructor
属性定义在prototype
对象上面,意味着可以被所有实例对象继承。
function P() {}
var p = new P();
p.constructor === P // truep.constructor === P.prototype.constructor // true
p.hasOwnProperty('constructor') // false
instanceof
instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例。
var v = new Vehicle();
v instanceof Vehicle // true
instanceof运算符的一个用处,是判断值的类型。
var x = [1, 2, 3];
var y = {};
x instanceof Array // true
y instanceof Object // true
利用instanceof
运算符,还可以巧妙地解决,调用构造函数时,忘了加new
命令的问题。
function Fubar (foo, bar) {
if (this instanceof Fubar) {
this._foo = foo;
this._bar = bar;
} else {
return new Fubar(foo, bar);
}
}
子类S
同时继承了父类M1
和M2
。这种模式又称为 Mixin(混入)。
function M1() {
this.hello = 'hello';
}function M2() {
this.world = 'world';
}function S() {
M1.call(this);
M2.call(this);
}// 继承 M1
S.prototype = Object.create(M1.prototype);
// 继承链上加入 M2
Object.assign(S.prototype, M2.prototype);// 指定构造函数
S.prototype.constructor = S;var s = new S();
s.hello // 'hello'
s.world // 'world'
域名与IP之间的对应关系,称为"记录"(record)。根据使用场景,"记录"可以分成不同的类型(type)。
常见的DNS记录类型如下。
(1)
A
:地址记录(Address),返回域名指向的IP地址。(2)
NS
:域名服务器记录(Name Server),返回保存下一级域名信息的服务器地址。该记录只能设置为域名,不能设置为IP地址。(3)
MX
:邮件记录(Mail eXchange),返回接收电子邮件的服务器地址。(4)
CNAME
:规范名称记录(Canonical Name),返回另一个域名,即当前查询的域名是另一个域名的跳转,详见下文。(5)
PTR
:逆向查询记录(Pointer Record),只用于从IP地址查询域名,详见下文。
在JS中,通俗来讲,闭包就是能够读取外层函数内部变量的函数。
使用闭包的注意点:
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。