js知识总结(构造函数和原型)
一:回顾
二:创建对象的几种方式:
1. 使用字面量的方式
a) 通过对象字面量创建对象的时候本质是在调用new Object
b) 通过数组字面量创建对象的进修本质是在调用new Array
c) 通过正则字面量创建对象的时候本质是在调用new RegExp
d) 代码示例:
var obj={
key:value
}
2. 使用函数封装
var createPerson=function(name,age){
var obj=new Object();
obj.name=name;
obj.age=age;
return obj;
}
var jack=createPerson(‘jack’,20);
3. 使用自定义构造函数
function Person(name,age){
this.name=name;
this.age=age;
}
var per1=new Person(‘jack’,20);
三:构造函数介绍:
1. 什么是构造函数:构造函数是用来初始化对象的
2. 构造函数的特征:
a) 首字母大写–规范
b) 构造函数一般和new关键字一起使用
c) 构造函数不需要手动的写返回值
3. 构造函数的执行过程:
a) 通过new关键字创建对象
b) 调用构造函数将构造函数中的this指向创建出来的对象
c) 在构造函数内部,通过this对对象新增属性和方法–初始化操作
d) 自动返回创建好的对象
4. 注意事项
a) 如果在构造函数中写了return语句
i. 如果返回了简单数据类型,不会对构造函数的返回值造成影响,依然是构造函数中创建的对象
ii. 如果返回了复杂数据类型,则会返回该复杂数据类型
b) 如果像调用普通函数一样调用构造函数
i. 函数中的this会指向window对象,通过this添加的所有属性和方法会添加到window对象以上
ii. 如果函数中没有return语句,则返回undefined
四:传统构造函数存在的问题:
公共方法
function sayHi(){
console.log("你们好!");
}
function Person(name,age){
this.name=name;
this.age=age;
this.sayhi=sayHi;
}
var per1=new Person('jack',20);
per1.sayhi();
var per2=new Person('rose',18);
per1.sayhi();
每一次创建对象都会将对象中的声明的属性都重新创建一次,造成浪费
var isEqual=per1.sayhi()===per2.sayhi(); 得到false
console.log(isEqual);
解决问题的第一种方式:提取公共方法--将函数的定义放到构造函数以外,那么在调用构造函数的时候,不会去 重复的创建,而是直接调用定义好的函数(地址的引用),这样就解决了资源浪费的问题
新的问题:
全局变量污染
代码结构混乱
五:原型:
1. 什么是原型:构造函数在创建出来的时候,系统会默认帮这个构造函数创建并且关联一个空的对象, 这个对象就是原型
2. 原型的作用:通过和原型关联的构造函数所创建出来的所有对象,都会共享原型中声明的属性和方法—(相当于后台语言中的父类)
3. 如何访问原型:可以通过构造函数名称.protoType获取原型。如:Person.prototype
4. 如何在原型中添加属性和方法:
a) 利用对象的动态特性为原型添加属性和方法 。具体可以通过类型.protoType来添加属性和方法,如:
Person.prototype.address=’广州’;
console.log(per.address);
b) 直接为构造函数的原型属性protoType赋值一个新的对象
Person.prototype={
gender:’male’,
phone:’12345’
}
var per2=new Person();
console.log(per2.phone);
六:使用原型注意事项:
1.原型的成员需要先添加再使用
2. 公共的成员才可以放到原型中,不同对象的不同的属性应该放在各自对象内
3. 在获取某些成员的时候,如果当前对象中没有,才会去原型中获取
4. 在使用对象设置属性的值的时候,只会在对象自身进行查找,如果有,就进行修改,如果没有就会新增
5. 在给构造函数的原型属性protoType赋值一个新的对象,会造成赋值前后创建的对象所指向的原型不一样,所以建议先赋值新对象之后再通过构造函数创建对象
七:proto属性的介绍
function Person(name,age){
this.name=name;
this.age=age;
}
var p=new Person('jack',20);
console.log(Person.prototype);
__proto__是一个非标准的属性,不同浏览器会有兼容的问题,所以不推荐在工作和项目中使用,只在自己调试代码的时候使用
console.log(p.__proto__);
console.log(Person.prototype === p.__proto__);
八:constructor属性:
function Person(){
}
constructor:指向和原型对应的构造函数
console.log(Person.prototype.constructor);
console.log(Person.prototype.constructor === Person); //true
注意:当构造函数.protoType重新赋值为一个新的时候,新的对象中需要手动新增constructor属性,让原型可以正常的和构造关联起来,但是constructor属性不是必须要修改的内容,如果没有重新指向,对使用也不会有影响,但是一般情况下,都推荐改成正常的指向。
九:面向对象的三大特性:封装,继承,多态
- 封装:将功能封装在对象中,对象会提供外部使用的接口,外界在使用的时候,只需要考虑如何调用接口,而不需要考虑内部的具体实现。
- 继承:js中的某些对象没有一些需要的属性和方法,但是另外的对象有,那么拿过来使用,就是继承
- 多态:js中没有多态,多态一般是体现在强类型的编程语言中。
十:js中继承的实现方式
1. 混入式(max-in)继承:通过循环将一个对象中的所有属性和方法混入到另外一个对象中:
#### 1.实现继承的第一种方式:混入式继承
var me={
work:function(){
console.log(‘打工’);
}
};
var mayun={
money:9999999999,
car:’QQ’,
manager:function(){
console.log(‘管理阿里巴巴。。。’);
}
}
遍历,让me对象拥有mayun对象的所有属性
for(var key in mayun){
me[key]=mayun[key];
}
console.log(me);
#### 2. 使用原型实现继承:
function Person(){
this.name=’人类’;
this.age=’20’;
this.sayHi=function(){
console.log(‘向你问好’);
}
}
function Painter(){
this.name=’画家’;
this.age=’50’;
this.canPaint=function(){
console.log(‘我会画画’);
}
}
var per=new Person();
var paint=new Painter();
function Student(){
}
2.1 修改构造函数的protoType属性来设置原型,但是原型只有一个,所以这种方式无法指定多种原型,后面指定的原型会覆盖前面指定的原型/
/Student.prototype=per;
var stu=new Student();
console.log(stu);
2.2 利用混入的方式给原型添加属性和方法
for(var key in per){
Student.prototype[key]=per[key];
}
for(var key in paint){
Student.prototype[key]=paint[key];
}
var stu=new Student();
console.log(stu);
十一:经典的实现继承的方式
1. Object.createy():
2. 解决兼容问题:
var source={
name:’jack’,
age:20
}
解决经典继承方式的兼容
function myCreate(){
判断浏览器是否支持经典继承方式
if(Object.create){
var son=Object.create(source);
return son;
}
else{
function UserDefine(){}
UserDefine.prototype=source;
var son=new UserDefine();
return son;
}
}
十二:继承的使用—扩展内置对象
- 如何安全的扩展内置对象–不能直接为系统的内置对象扩展属性和方法,因为它是共用的
1.创建自定义的构造函数
2.实现原型继承,如继承自一个数组对象
3.为自定义的构造函数的原型扩展属性和方法
4.通过自定义的构造函数创建出的对象,既拥有系统数组的属性和方法,又拥有自定义的属性和方法
5.自定义的类型不会对其它的类型产生影响 示例:
使用js内置对象
var arr1=new Array();
为系统的内置对象添加一些自定义属性或方法
Array.prototype.sayHi=function(){
console.log(‘我是第一个对象’);
}
arr1.sayHi();创建第二个对象
var arr2=new Array();
为第二个对象添加自定义的属性或方法
Array.prototype.sayHi=function(){
console.log(‘我是第二个对象’);
}
arr2.sayHi();
但是,如果再用第一个对象来进行属性或方法的调用,则发现覆盖
arr1.sayHi();解决:创建自定义数组,为当前数组添加独立的属性或方法,而不是去修改共享的内置对象,否则修改一个,其它使用的时候也发生改变
function MyArray(){}
让自定义的类型是一个数组–拥有数组的属性和方法
MyArray.prototype=[];
MyArray.prototype.sayHi=function(){
console.log(‘对象1向你问好’);
}
使用自定义类型创建对象
var arr3=new MyArray();
arr3.push(1,2,3,4,5);
console.log(arr3);
arr3.sayHi();
arr2.sayHi();
arr1.sayHi();
十三:原型链:
- 概念:对象都有原型,原型是一个对象,所以原型又有原型,这样就形成了一个原型链
- 属性搜索原则:当使用对象去访问一个属性或者方法的时候
a) 先在对象自身中进行查找,如果找到就直接使用
b) 如果没有找到,就去对象的原型中进行查找,如果找到直接使用
c) 如果没有找到,就沿着原型链依次向上查找,直到找到null