一、构造函数
1、构造函数是一个模板。优势:可复用
构造函数的作用 : 初始化由new创建出来的对象.
如果一个函数跟new一起使用,此时成为构造函数。就是一个普通函数,任何函数都可以作为构造函数。
构造函数,是一种函数,主要用来在创建对象时对 对象 进行初始化(即为对象成员变量赋初始值),并且总是与new运算符一起使用。
2、new 运算符
new运算符创建一个新对象。关键字new后跟随一个函数调用。
这里的函数称做:构造函数(constructor),构造函数用以初始化一个新创建的对象。
new的作用:创建一个新对象(实例)(空对象).
*注:这个对象可以是用户自定义的,也可以是系统自带的*
3、 创建对象步骤
①new 申请内存, 创建对象
②将新对象的引用赋值给 this (obj = this)
③调用构造函数,利用 this.成员=值 来给对象添加成员(当然,如果有其他的代码也会执行)
④ 返回新对象
4、构造函数调用说明
①先创建对象再调用构造函数
②new用来创建对象,原型确定
③构造函数用来初始化这个对象
④构造函数会自动返回这个新对象
构造函数调用和普通的函数调用以及方法调用的不同主要体现在:
①参数处理:没有形参,可以省略实参列表和圆括号。②this指向。③返回值
⑤新创建的空对象继承自构造函数的prototype属性
⑥对象在被创建的那一刻原型就定下来了。
5、构造函数返回值
a. 如果显式地使用return返回一个对象,那么这个对象就会代替新创建的对象被返回
b. 如果使用return但没有指定返回值或者返回一个原始值,那么此时将会忽略返回
6、方法放在构造函数中的缺点:浪费内存
构造函数中的属性和方法,会在调用的时候被处理,即会消耗内存。
new 出来的对象,都是一个单独副本。 new 一个对象就占用一份内存,new 多少对象就占用多少份内存。
对于构造函数中的方法来说,每个对象的方法都是相同的(或者说应该是被共享的)
原则:将公共的属性和方法放到 prototype 中。
7、 构造函数使用注意点
①构造函数的名字一般以大写字母开头(推荐)。②使用new创建一个新对象。
③如果想让创建出来的对象具有某些属性,需要在函数内部通过。this.属性名=值来设置。
④构造函数不需要返回值,会默认返回一个对象。
二、原型(prototype)
原型的说明:
只要是函数就有 prototype 属性,即函数的原型属性;
函数的原型属性(prototype)的是 对象类型;
由 构造函数 创建出来的对象,会默认链接到其构造函数的这个属性(prototype)上;
构造函数的 prototype 属性的作用就是:实现数据共享
实例:由构造函数创建出来的对象. 对象:泛指javascript中的复杂数据类型.
prototype与_proto_相同点和不同点:
相同点:1.这两个都是属性,储存的是对象的引用
2.这两个属性的值就是同一个对象。
不同点:1.站在不同的角度去看待这两个属性
prototype是站在构造函数的角度;__proto__是站在对象的角度
2.__proto__是非标准属性
原型属性和原型对象:
是同一个东西;只是站在不同角度去看到这一个东西,对它的不同叫法;目的是为了更加准确地让我们知道指的是谁;原型属性是站在构造函数的角度去看待;原型对象是站在对象的角度看。
三、获取对象的类型
为什么要获取对象的类型?
因为一个方法里面可以根据参数类型的不同实现不同的功能。
一般情况下,使用构造函数的名字描述一个对象的类型。因此,获取对象的类型转化为获取构造函数的名字。
获取对象 p 的类型,只需要能获取到 "Person" 就获取到类型了。
方式:1 name(兼容性)。2 字符串截取。3 正则表达式。
四、constructor 构造函数/构造器
任何一个函数的原型默认情况下,都有一个 constructor,这个属性 默认 就指向了 构造函数。
为什么通过对象p就能够放到 constructor 这个属性?
1、 constructor 是谁提供的? Person.prototype 2、 属性搜索原则 console.log(p.constructor);
访问到 constructor 有以下几种方式:
① Person.prototype.constructor
② p.constructor//此方法是通过属性搜索原则在原型中搜索到的
③ p.__proto__.constructor
属性搜索原则:
① 先在对象本身找,有没有该属性,如果有直接返回,如果没有
② 在原型对象中查找,如果有直接返回,如果没有
③ 就在原型对象的原型对象中查找
④ 如果最后也没有找到,此时,结果就是:undefined。
⑴每个对象都有一个constructor
属性。 ⑵ constructor
描述的是对象的构造函数。
⑶ p.constructor === Person 结果为true。⑷ 对象的constructor
属性是由构造函数的prototype提供的。
属性修改和读取的区别:
⑴获取操作:遵循属性查找原则。
⑵赋值操作:(只与当前对象有关)。只会操作对象本身,如果对象中没有该属性则创建该属性,并赋值; 如果对象中有,则修改。
五、面向对象
面向对象语言:c++系的语言:c++/c#/java/object-c/swift
javascript是一门弱类型的语言:1.javascript中是有类型的2.不受类型的约束
强类型:变量的类型是不可变的。
使用 typeof 运算符:无法准确获取对象的类型。
面向对象的三大特征:封装、继承、多态。
封装:包装/包裹,将复杂的代码逻辑包裹起来,将简单的东西暴露(留)在外面。
函数是对具体过程的封装;对象是对行为和数据的封装。
继承:自己没有,别人有,拿过来让其成为自己的或者自己可以用的。子类可以使用从父类继承的属性和方法。
即:让某个类型的对象获得另一个类型的对象的属性的方法。
继承的方式:1.原型继承 2.混入继承(对象o继承其他的对象o1,o2....)
原型继承:
为什么将属性和方法放到对象的原型中就能实现继承?
对象 继承自 原型对象,是对象和原型对象之间的关系。
给对象的原型对象添加的任何属性和方法,对象都可以直接拿过来使用。
给原型中添加属性:
1 利用对象的动态特性 添加属性和方法
对象的动态特性:
var obj = {};
obj.name = "jack";
console.log(obj.name);
obj.age = 18;
console.log(obj.age);
Person.prototype.sayHi = function() {
alert("你好");
};
Person.prototype.coding = function() {
alert("hello world");
};
// Person.prototype......
var p = new Person();
2 覆盖原型对象 添加属性和方法
覆盖原型对象:
var obj = {
// 如果对类型要求不严格,那么可以省略,但是,如果对类型的要求严格,此时,需要手动添加一 个。属性:constructor,并指向其构造函数
constructor: Person,
name: "rose",
age: 18,
sayHi: function() {
alert("where is jack");
}
};
Person.prototype = obj;
//Person.prototype = {
constructor: Person,
name: "rose",
age: 18,
sayHi: function() {
alert("where is jack");
}
};//
var p = new Person();
var t = getType(p.constructor);
console.log(t);
function getType(fn) {
return (typeof fn !== "function") ? undefined : (fn.name || /function (.+)\(/.exec(fn + "")[1]);
}
3 混入式继承 添加属性和方法
混入式继承 :
Person.prototype.extend = function(obj) {
for(var k in obj) {
this[k] = obj[k]; // 谁调用这个方法,this 就是谁
}
};
var o = {
name: "rose",
age: 18,
sayHi: function() {
alert("where is jack");
}
};
// 这是在给原型对象添加属性和方法
Person.prototype.extend(o);
var p = new Person();
// 这是给对象p本身添加属性和方法
p.extend({
name: "rose",
age: 18,
sayHi: function() {
alert("where is jack");
}
});
以上两种调用方式的区别:
如果是共有的属性和方法,就使用原型来调用 extend 添加属性或者方法
如果是某个对象特有的属性或者方法,直接使用对象来调用 extend
var p1 = new Person();
经典继承:
新对象 = Object.create(被继承的对象);// o1 继承自 o2 ,o2被继承。var o1 = Object.create(o2);
Object.create的作用:1 创建新对象。2 新对象继承自参数对象。
var o1;
var o2 = {
name: "张三",
age: 19
};
// o1 继承了 o2 中的属性和方法,是放在o1的原型中的。
o1 = Object.create(o2); console.log(o1);
目的:Object.create 存在兼容性问题,解决兼容性问题
实现兼容的方式:
1 直接在Object对象中修改
2 写一个新函数 判断是否支持这个方法,如果支持就拿来用,如不过不支持,就自己实现。
var o1;
var o2 = {
name: "张三",
age: 19
};
// 方式一
// 判断是否支持这个方法
if(!Object.create) {
// 不支持
Object.create = function(obj) {
function F() {}
F.prototype = obj;
var f = new F();
return f;
};
}
var o1 = Object.create(o2);
// o1 继承自 o2, 是放在 o1 的原型对象中
// 1、 如何创建一个对象?
// var o = new Object(); (不能这么直接创建一个Object类型的对象)
// function F() {}
// var o = new F();
// 2、 o2中的属性要放到 o 的原型对象中的去
// o.__proto__ 不能这么做
// F.prototype = o2;
方式二 :(推荐使用)
function create(obj) {
// 1 、判断是否支持Object.create
if("create" in Object) {
// 1.1 支持
return Object.create(obj);
}
function F() {}
F.prototype = obj;
return new F();
}
var o2 = {
n: 1,
m: 2
};
var o1 = create(o2);
多态:(基于强类型,js中没有这个特征)把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差 异,写出通用的代码,做出通用的编程,适应需求的不断变化。
继承总结:结论
1、 只有函数才会有 prototype,所有的函数都有一个prototype属性(一些内置的方法没有,例: function toString())。
2 、只要是对象就有 __proto__。
3 、构造函数.prototype 跟 对象.__proto__ 就是同一个东西。
4、 对象 继承自 原型对象。
5、 构造函数的prototype 是一个对象类型。
6、 默认情况下,对象的原型对象是一个具有:constructor属性的对象。
7、 构造函数.prototype 默认继承自 Object.prototype。
8、当函数被创建的时候,prototype属性会自动创建和初始化。
function Person() {}
var p = new Person();
console.log(Person.prototype.__proto__ === Object.prototype);
// Object 是一个函数,函数也是对象
继承的实现过程:
1 、要创建对象就要有构造函数 (前提:要实现继承)
var o = {};
o.__proto__ === Object.prototype
① __proto__ 非标准的属性,不能用在实际的开发环境中② 所有的对象就都会有这个属性或者是方法
functioin F() {}
F.prototype = 参数;
var f = new F();
2 、如何让f继承自另外一个对象?
前提:f 默认就会继承自 F.prototype
如果不知道如何让f继承自另外一个对象,只需要改变 默认原型对象的指向即可 F.prototype = 另外一个对象