构造函数

  1. 构造函数是特殊的函数,里面没有returen返回值
  2. new的过程中,会自动将对象返回,不需要return
  3. new的过程中,会执行函数中的代码,会将创建的对象赋值给构造函数中的this

基本概念

  1. 通过同一个构造函数创建的对象都会关联一个神秘的对象,可以通过构造函数.prototype进行访问,这个神秘对象被称为原型对象
  2. 这个原型对象可以被用来做继承用,js中的继承有好几种,包括混入继承,经典继承,还有原型继承
  3. 通过构造函数创建出来的对象,不仅拥有构造函数中的属性,还拥有原型对象中创建出来的属性
  4. 实例化后的对象也可以通过__proto__进行访问原型对象,但是只是调试时使用,不推荐正式代码中使用

继承方式

原型基本

<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
//不仅拥有构造函数中的属性,还拥有原型中创建出来的属性
Person.prototype.gender = 'male';
var p = new Person('qx', 18);
console.log(p.name);//qx
console.log(p.age);//18
console.log(p.gender);//male
</script>

混入继承

<script>
var o = {}
var obj = {
name: "张三",
age: 18,
sayHello: function () {
console.log("Hello world");
}
}
//混入式继承
for (var k in obj) {
o[k] = obj[k];
}
console.log(o);
</script>

经典继承

1、最早的原理

<script>
//通过替换原型,使得被创建出来对象也拥有传入对象的属性
function jicheng(obj) {
var o = {};
o.__proto__ = obj;
return o;
} var o = jicheng({name: "张三"});
console.log(o);
</script>

2、create方法

<script>
var o = {
name: "周三"
}; var obj = Object.create(o);
console.log(obj.name);
</script>

3、create方法存在兼容性问题

<script>
var obj = {
name:"周三"
}; //检测浏览器的能力,如果没有Object.create方法就给他添加一个(不推荐使用)
if(Object.create){
var o = Object.create(obj);
}else{
Object.create = function () {
function F() {
}
F.prototype = obj;
var o = new F();
}
var o = Object.create(obj);
}
</script>
<script>
//自己定义个函数
function create(obj) {
if (Object.create) {
return Object.create(obj);
} else {
function F() {
} F.prototype = obj;
return new F();
}
}
</script>

原型对象

  • 原型对象可以通过构造函数.prototype获得
  • 原型对象中的属性和方法,可以提供给那些通过此构造函数创建的对象使用,达到了全局使用的作用
  • 原型对象被替换,如果替换的属性与原有构造函数的属性相冲突,那么被创建的对象,依然首先访问的是构造函数中的属性
<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say=function () {
console.log('chi');
}
var p=new Person('wx',18);
p.say();//chi
</script>
  • 原型对象添加属性和方法还可以通过以下方式,用{}来追加,{}这是字面量,同时代表了一个字面量对象,Person.prototype可以直接点出{}里面的属性和方法
<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype={
say:function () {
console.log('chi');
},
play:function () {
console.log('wan');
}
};
var p=new Person('wx',18);
p.say();//chi
p.play();//wan
</script>
  • 原型对象在没有被替换前会输出一个constructor,其指向就是自己的构造函数,但是一旦被替换就会变成失去原本的构造函数,确实有,但是其指向的是Object的构造函数,最好最好的方式是在原型对象被替换之后,再次添加一个constructor属性,并且指向自己原先的构造函数
  • 补充下,为什么原型对象被替换后,其构造函数属性指向的是Object的构造函数?因为Person.protype,被称为原型对象,既然是对象,肯定也是通过某一个构造函数创建出来的,结果就是Object构造函数,所以Person.prototype有Object的构造函数

<script>
function Person(name, age) {
this.name = name;
this.age = age;
} console.dir(Person.prototype);
Person.prototype = {
say: function () {
console.log('chi');
},
play: function () {
console.log('wan');
}
};
Person.prototype.constructor = Person;
console.dir(Person.prototype);
</script>

原型继承

将animal这个对象赋值给Person.prototype,与上面的通过字面量{}道理是一样的,毕竟animal也是一个对象,里面的属性也是被{}包裹的

<script>
function Animal() {
this.eat = 'chifan';
} var animal = new Animal(); function Person() {
this.read = 'dushu';
} var p1 = new Person(); //person显示是具有animal的属性,毕竟人也是动物,具体方法是让person的原型被替换成animal
//替换后,通过person构造函数创建出来的对象,不仅具有person的属性,还具有animal的属性 Person.prototype = animal;//Person.prototype = new Animal()这样也行 var p2 = new Person();
console.log(p1);
console.log(p2);
</script>

输出结果:

  1. p1与p2输出的都是Person类,所以都具有read属性
  2. p1与p2输出的原型__proto__中的constructor构造函数属性不一样,p1指向的是Person构造函数,p2指向的是Animal构造函数而且具有eat属性
  3. p1与p2输出的原型__proto__中的原型proto__指向都是一致的,都为Object类

结果分析:

  1. p1与p2都是通过构造函数Person创建出来的,所以都具有read属性
  2. p1与p2创建的中间,执行了 Person.prototype= new Animal() 代码,所以p2的原型被替换了,导致p2的原型__proto__中指向的是Animal类,其构造函数当然也就是Animal,所以也具有eat属性
  3. p1与p2追踪到最后其实都是属于Object类,再追踪的话,Objec类的原型就是null
  4. 需要注意的是在执行 Person.prototype= new Animal() 代码之后,所有被创建出来Person对象的原型对象的构造函数constructor将不再指向原本的Person,而是Animal,所以最好将改过来,Person.prototype.constructor=Person

复杂原型继承

<script>
function Yuanzi() {
this.des = '进化';
} function Animal() {
this.skill = '捕猎';
} function Human() {
this.say = '说话';
} Animal.prototype = new Yuanzi();
Animal.prototype.constructor = Animal;
Human.prototype = new Animal();
Human.prototype.constructor = Human; var h = new Human();
console.log(h);
</script>

打印对象h:

  1. h是一个Human类,原型对象指向Animal类,原型对象的构造函数又指向Human
  2. Animal类的原型对象指向的是Yuanzi,其构造函数又指向Animal
  3. h所以拥有了Human类,Animal类的所有属性

构造器的作用

  • 构造器其实就是构造函数,实例化后的对象是可以通过对象.constructor的方式访问自己的构造函数的,这个属性其实是实例化后的对象本身没有,但是原型对象上有,那么它为什么被创建出来呢?
  • 其实很简单的,因为构造函数可以被看作是类,在简单类型中,var a=‘hello’ 或者 var b=1 他们的typeof类型都是可以找到的对应的内置对象(类),那么复杂类型呢?比如一个通过Person构造函数创建的对象通过typeof打印结果是object,通过Animal构造函数创建出来的对象通过typeof打印结果也是object,那么这些对象的类到底是什么呢?答案是constructor,有了它就可以让对象知道创建自己的构造函数是什么,等同于知道了自己属于什么类

原型链基本概念

1、每个构造函数都有原型对象

2、每个对象都会有构造函数

3、每个构造函数的原型都是一个对象

4、那么这个原型对象也会有构造函数

5、那么这个原型对象的构造函数也会有原型对象

6、这样就会形成一个链式的结构,称为原型链

7、通过修改原型链结构实现的继承,就叫做原型继承

8、三角关系到成立的必要性之一是 实例化对象p原型的原型与Object构造函数都是指向同一个对象(在内存中的内容与地址都是一样的)

consolo.log (p.__proto__.__proto__===Object.prototype)//true

属性搜索基本原则

  1. 当访问一个对象的成员的时候,会现在自身找有没有,如果找到直接使用,
  2. 如果没有找到,则去当前对象的原型对象中去查找,如果找到了直接使用
  3. 如果没有找到,继续找原型对象的原型对象,如果找到了,直接使用
  4. 如果没有找到,则继续向上查找,直到Object.prototype,如果还是没有,就报错

 原型对象的替换

上面负责原型继承的模式是进行的原型的替换,这样虽然方便了,但是也是有问题的

(1)给原型对象替换赋值,原型对象就会失去constructor属性

<script>
function Person(name, age) {
this.name = name;
this.age = age;
} var p = new Person('qx', 18);
console.log(Person.prototype);
Person.prototype = {
say: function () {
console.log(this.name);
}
};
console.log(Person.prototype);
</script>

(2)当然即使替换了原型对象,但是实例化后的对象依然具有constructor属性,而这个属性属于对象的原型对象的原型对象,从上图可以看出,以及下图也看出,替换前后打印的结果是不一样的。替换后,访问的是对象的原型对象的原型的构造函数

<script>
function Person(name, age) {
this.name = name;
this.age = age;
} var p = new Person('qx', 18);
console.log(Person.prototype.constructor);
Person.prototype = {
say: function () {
console.log(this.name);
}
};
console.log(Person.prototype.constructor);
</script>

(3)对原型对象进行替换,其实可以看作是将一个对象赋值给了一个普通对象,类似var a={},我们打印a,consolo.dir(a),结果是a并没有构造函数的属性

(4)解决办法就是,替换了原型对象,必须在替换的对象上面加上constructor属性,并且赋值指向的是自身构造函数,和上面的复杂原型继承是一样的

<script>
function Person() { }
Person.prototype = {
constructor: Person;
}
</script>

扩展内置对象

  • 确实可以通过修改内置对象的原型对象添加一些方法,但是直接添加是不安全的,安全的方式是通过一个中介,理由是内置对象是系统默认的,直接修改它是不安全的,让一个中介拥有内置对象的所有属性,再通过这个对象的原型对象添加方法是保险的
<script>
function MyArray() {
// this.name = '我是一个数组';
} MyArray.prototype = new Array();
var arr1 = new MyArray();
arr1.push(4)
console.log(arr1);
MyArray.prototype.say = function () {
console.log('添加一个说的方法');
}
var arr2=new MyArray();
arr2.say();
</script>

JS高级——原型链的更多相关文章

  1. js高级-原型链

    JavaScript是基于原型的面向对象的语言(相当于一个基类,父类),而不是像Java通过类模板构造实例,通过原型实现属性函数的复用 函数都有 prototype属性 指向函数的原型对象 只有函数根 ...

  2. js javascript 原型链详解

    看了许多大神的博文,才少许明白了js 中原型链的概念,下面给大家浅谈一下,顺便也是为了巩固自己 首先看原型链之前先来了解一下new关键字的作用,在许多高级语言中,new是必不可少的关键字,其作用是为了 ...

  3. 前端基本知识(二):JS的原型链的理解

    之前一直对于前端的基本知识不是了解很详细,基本功不扎实,但是前端开发中的基本知识才是以后职业发展的根基,虽然自己总是以一种实践是检验真理的唯一标准,写代码实践项目才是唯一,但是经常遇到知道怎么去解决这 ...

  4. 怎么理解js的原型链继承?

    前言 了解java等面向对象语言的童鞋应该知道.面向对象的三大特性就是:封装,继承,多态. 今天,我们就来聊一聊继承.但是,注意,我们现在说的是js的继承. 在js的es6语法出来之前,我们想实现js ...

  5. js 高级 原型与原型链

    * 所有函数都有一个特别的属性: * `prototype` : 显式原型属性* 所有实例对象都有一个特别的属性: * `__proto__` : 隐式原型属性 1.  每个函数都有一个prototy ...

  6. JS高级---原型和原型链

    原型和原型链 原型链是一种关系, 实例对象和原型对象之间的关系,关系是通过实例对象中浏览器使用的原型(__proto__)来联系的 自定义构造函数,通过实例化,创建实例对象 实例对象中__proto_ ...

  7. 一篇文章理解JS继承——原型链/构造函数/组合/原型式/寄生式/寄生组合/Class extends

    说实在话,以前我只需要知道"寄生组合继承"是最好的,有个祖传代码模版用就行.最近因为一些事情,几个星期以来一直心心念念想整理出来.本文以<JavaScript高级程序设计&g ...

  8. 自己对js对原型链的理解

    js对象分为2种 函数对象和普通对象 函数对象 比如 function Show(){}var x=function Show2(){}var b=new Function("show3&q ...

  9. JS中原型链继承

    当我们通过构造函数A来实现一项功能的时候,而构造函数B中需要用到构造函数A中的属性或者方法,如果我们对B中的属性或者方法进行重写就会出现冗杂的代码,同时写出来也很是麻烦.而在js中每个函数都有个原型, ...

随机推荐

  1. 【BZOJ4514】数字配对(费用流)

    题意: 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci× ...

  2. CF576D. Flights for Regular Customers

    n<=150个点,m<=150条路,每条路Ai,Bi,Di表示Ai到Bi有一条有向边,使用他前至少要走Di条路,问1到n最少走几条路. 又是n^4过150的题.... 不同于传统的最短路, ...

  3. java中split以。点和|分割的问题

    问题:想要按照点来切分字符串直接这样 String[] filep=filename.split("."); 结果得到一个空数组 解决方法: 法一:需要转义,改为:(注意是2个\\ ...

  4. 放大的X

    Problem Description 请你编程画一个放大的’X’.如3*3的’X’应如下所示: X X XX X 5*5的’X’如下所示:X X X X X X XX X   Input 输入数据第 ...

  5. ArcGIS 10 Engine DevelopKit 之安装与帮助;VBA,跨平台CPP,JAVA,ArcGIS Engine DevelopKit 10 的帮助如何打开

    你看到的这个文章来自于http://www.cnblogs.com/ayanmw ArcGIS Engine Developer Kit10 可以从VeryCD上下载到.其不需要破解,只需要你有一个已 ...

  6. linux下uart应用编程

    目的:在用户空间通过读写uart设备文件,控制uart串口发送和接收数据. 在用户空间设置uart波特率.奇偶校验使能等操作是通过termios结构体和termios库函数完毕.须要在应用程序中包括t ...

  7. http://www.html5tricks.com/demo/jiaoben2255/index.html 排序算法jquery演示源代码

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.or ...

  8. [vs执行报错] CRT detected that the application wrote to memory after end of heap buffer

    CRT 是c/c++ run-time lib , 是程序执行时所需的核心库. 这个错误是由于以对内在操作的过程中.所写的地址超出了.所分配内在的边界 有个建议是: 1.内存申请多少释放多少,释放掉你 ...

  9. scikit-learn: isotonic regression(保序回归,非常有意思,仅做知识点了解,但差点儿没用到过)

    http://scikit-learn.org/stable/auto_examples/plot_isotonic_regression.html#example-plot-isotonic-reg ...

  10. 一看就懂系列之 由浅入深聊一聊php的垃圾回收机制

    前言 是的,平时经常听到大牛说到的gc,就是垃圾回收器,全称Garbage Collection. 早期版本,准确地说是5.3之前(不包括5.3)的垃圾回收机制,是没有专门的垃圾回收器的.只是简单的判 ...