构造函数

  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. 51Nod——T 1631 小鲨鱼在51nod小学

    https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1631 基准时间限制:1 秒 空间限制:131072 KB 分值: 20 ...

  2. 洛谷—— P1657 选书

    https://www.luogu.org/problem/show?pid=1657 题目描述 学校放寒假时,信息学奥赛辅导老师有1,2,3……x本书,要分给参加培训的x个人,每人只能选一本书,但是 ...

  3. Nexus设备升级5.0方法

    1. 从该页面为您的设备下载适当的系统映像.然后将它解压缩到一个安全的文件夹. 2. 通过 USB 连接到您的计算机. 3. 使用下列的方法,在fastboot mode下启动设备: 使用 adb   ...

  4. FloatingActionMenu 向上弹出菜单

    本人在github上找到了一个FloatingActionsMenu,精简了其效果(原效果有上下左右四个方向)仅仅保留向上的效果,并做了一定的优化. github上的源代码:地址 ,精简后的源代码地址 ...

  5. 我不常用的 javascript

    获取当前时间:new Date    (最后的调用括号可加可不加) 获取当前时间戳: 方法1:Date.parse(new Date()) 方法2:(new Date()).valueOf() 方法3 ...

  6. Combining an audio file with video file in python

    Combining an audio file with video file in python - Stack Overflow https://stackoverflow.com/questio ...

  7. ios8--加载图片

    // // ViewController.m // 06-资源存放问题 #import "ViewController.h" /** 加载图片的方式: 1. imageNamed: ...

  8. CNN 文本分类模型优化经验——关键点:加卷积层和FC可以提高精度,在FC前加BN可以加快收敛,有时候可以提高精度,FC后加dropout,conv_1d的input维度加大可以提高精度,但是到256会出现OOM。

    network = tflearn.input_data(shape=[None, max_len], name='input') network = tflearn.embedding(networ ...

  9. git 命令 —— checkout

    git checkout 会重写工作区.check in 常常表示酒店入住,则 check out 就表示结账(检查)离开. 1. 基本用法 Git学习笔记04–git checkout git ch ...

  10. 第四周 Leetcode 124. Binary Tree Maximum Path Sum (HARD)

    124. Binary Tree Maximum Path Sum 题意:给定一个二叉树,每个节点有一个权值,寻找任意一个路径,使得权值和最大,只需返回权值和. 思路:对于每一个节点 首先考虑以这个节 ...