js(2)知识小总结

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属性不是必须要修改的内容,如果没有重新指向,对使用也不会有影响,但是一般情况下,都推荐改成正常的指向。

九:面向对象的三大特性:封装,继承,多态

  1. 封装:将功能封装在对象中,对象会提供外部使用的接口,外界在使用的时候,只需要考虑如何调用接口,而不需要考虑内部的具体实现。
  2. 继承:js中的某些对象没有一些需要的属性和方法,但是另外的对象有,那么拿过来使用,就是继承
  3. 多态: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. 如何安全的扩展内置对象–不能直接为系统的内置对象扩展属性和方法,因为它是共用的
    1.创建自定义的构造函数
    2.实现原型继承,如继承自一个数组对象
    3.为自定义的构造函数的原型扩展属性和方法
    4.通过自定义的构造函数创建出的对象,既拥有系统数组的属性和方法,又拥有自定义的属性和方法
    5.自定义的类型不会对其它的类型产生影响
  2. 示例:

    使用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();

十三:原型链:

  1. 概念:对象都有原型,原型是一个对象,所以原型又有原型,这样就形成了一个原型链
  2. 属性搜索原则:当使用对象去访问一个属性或者方法的时候
    a) 先在对象自身中进行查找,如果找到就直接使用
    b) 如果没有找到,就去对象的原型中进行查找,如果找到直接使用
    c) 如果没有找到,就沿着原型链依次向上查找,直到找到null
-------------本文结束感谢您的阅读-------------