javascript面向对象设计

2023-05-16

javascript和java语言不一样,它没有类这个说法,更没有子类父类一说,所以它所谓的继承机制,也是和其他语言完全不同的。

创建对象三种方式

1.最简单的方式,创建一个object对象,然后为它添加属性和方法

var person=new object();
person.name="zhangsan";
person.age=20;
person.sayName=function(){alert(this.name);};

2.工厂方式

本质上就是对上述最简单的方式的包装。

function createPerson(name,age){
var person=new object();
person.name=name;
person.age=age;
person.sayName=function(){
alert(this.name);
};
return person;
}

缺点是不知道对象的类型。

3 构造函数模式

即定义一个函数,该函数作为类似类中的构造函数,用于生成对象

function Person(name,age){
this.name=name;
this.age=age;
this.sayName=function(){
alert(this.name);
};
}

通过new方式创建对象,

var person1=new Person("zhangsan",20);

该方式经历4个步骤

1,创建一个新对象

2,函数的作用域付给力新对象,this就表示了新的对象

3,执行函数中的代码,为新对象设置属性

4,返回新对象。

构造函数也是普通函数,任何函数只要用new操作符调用,都可以成为构造函数。

Person作为普通函数调用的话,this就是window对象。

Person("zhangsan",20);
window.sayName();

缺点是太浪费资源,每个实例中的属性都是不同的,特别是方法不能复用:

var person1=new Person("zhangsan",20);
var person2=new Person("zhangsan",20);
person1.sayName==person2.sayName// false

4,原型模式

每个函数都有一个prototype属性,定义在其中的属性,被各个实例对象所共享。也就是说,实例对象的属性和方法,分成两种,一种是自己定义的,另一种是prototype中共享的。 以Person为例子,按原型模式申明类:

function Person(){}Person.prototype.name="zhangsan";
Person.prototype.age=20;
Person.prototype.sayName=function(){alert(this.name);
};
var person1=new Person();
var person2=new Person();

此时person1这的属性和person2中的属性是指向同一个引用。

person1.sayName==person2.sayName// true

理解原型

constructor属性,函数的prototype属性有一个constructor属性,该属性的值默认情况下指向该prototype属性所在的函数。比如:

Person.prototype.constructor

function Person(){}

看一个图,更明白点

1 (2).jpg

其中__proto__是内部属性,javascript脚本中是没有的,只能通过chrome调试的时候才能看到。所有实例对象的__proto__都指向其构造器的prototype。

person1和person2对象的__proto__内部属性都指向了Person的原型,和Person自身没有任何关系。

当person1的sayName方法调用的时候,实际会执行2次查找,现在person1实例上找sayName,找不到,会去person1指向的原型上去找。

上述可知道,如果在person1上也找到sayName方法,那么原型上的方法就无法被调用到了。

通过方法 hasOwnProperty可以判断一个方法是在实例中还是在原型中。

再次强调,实例中的指针仅指向原型,而不是指向构造函数。

通过快捷写法

function Person(){}
Person.prototype.name="zhangsan";
Person.prototype.age=20;
Person.prototype.sayName=function(){alert(this.name);};

这种写法比较罗嗦

可以用json方式来写:

function Person(){}
Person.prototype={
name:"zhangsan",
age:20,
sayName:function(){alert(this.name);}
}

这种写法比较简洁,但是有一个问题,就是constructor属性的只不再指向Person了,这种语法等于完全重写了默认的prototype,因此constructor属性也被指到Object构造函数了。如果有必要的话,需要把constructor值手动赋值:

function Person(){}
Person.prototype={
name:"zhangsan",
age:20,
sayName:function(){
alert(this.name);
}
}

原型模式缺点,就是由于其共享性。它的所有属性是共享的,对于函数来说,求之不得,对于字段属性,这种共享可能会引起大问题。因此,单独使用原型模式很少见。

取长补短

同时使用构造函数模式和原型模式,非常简单,把需要共享的放到原型中,不需要共享的放到构造函数中。

function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype={
sayName:function(){
alert(this.name);
}
}

这种方式是目前最流行的方式。

还有动态原型模式,寄生构造函数模式,稳妥构造函数某事,不作介绍。

继承(主要两种方法,原型链和call/apply)

原型链,利用原型让一个引用类型继承另一个引用类型的属性和方法。

如果原型对象指向了另一个类型的实例,那么原型对象指向实例-->指向另一个原型对象-->constructor指向所在的函数。

这个就是所谓的原型链。如下图中SuperType和SubType展现的那样,SubType继承了SuperType。

2.jpg

上图中可以看到,原型存在于SuperType的实例中的所有属性和方法,都能在SubType中访问到了。

用代码表示如下:

function SuperType() {
this.property =true;
}
SuperType.prototype.getSuperValue =function() {
    returnthis.property;}
    function SubType() {
    this.subproperty =false;
}
//发生继承行为
SubType.prototype =new SuperType();
SubType.prototype.getSubValue =function() {
    returnthis.subproperty;
​​​​​​​}
    var instance =new SubType();
    alert(instance.getSuperValue()); //true
    alert(instance instanceofObject); //true
    alert(instance instanceof SuperType); //true
    alert(instance instanceof SubType); //true
    alert(Object.prototype.isPrototypeOf(instance)); //true
    alert(SuperType.prototype.isPrototypeOf(instance)); //true
    alert(SubType.prototype.isPrototypeOf(instance)); //true

代码中,重写了SubType的原型,而不是用原来默认的。由于property是一个实例属性,getSuperValue是一个原型方法。所以property会出现在SubType Prototype(SuperType的实例)中,而getSuperValue不会出现。instance的constructor现在只想的SuperType。

还有一个要提及的是,Javascript中,所有的函数默认的原型都是Object的实例,SuperType函数的prototype也是指向Object Prototype。 因此通过这样一个链条访问属性的时候,还是会通过搜索机制顺藤摸瓜的找到对应的属性。

原型链的缺点很明显,所有的子类的属性是共享的。这个缺点是致命的,因此实践中很少单独使用原型链。

call/apply方法这种方法称为constructor stealing(借用构造函数)。

call方法的作用,官方解释 call方法:

语法:

call([thisObj[,arg1[, arg2[, [,.argN]]]]])

定义:调用一个对象的一个方法,以另一个对象替换当前对象。

说明: call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。 如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。

我的理解,就是把原有对象中的代码,放到thisObj中运行,代码中如果出现this,代表的就是thisObj中。

举个例子:

function Animal(){  
    this.name = "Animal";  
    this.showName = function(){  
        alert(this.name);  
    }  }  
    /**定义一个Cat类*/  
 function Cat(){  
    this.name = "Cat";  }  
  /**创建两个类对象*/  
  var animal = new Animal();  var cat = new Cat();  
  //通过call或apply方法,将原本属于Animal对象的showName()方法交给当前对象cat来使用了。  
  //输入结果为"Cat"  
  animal.showName.call(cat,",");  
  //animal.showName.apply(cat,[]);

关于call和apply方法的概念,可以自行网上查阅,其中call和apply的差别在于调用时候的参数。 call调用时,参数为用逗号分隔的一组值,而apply调用的时候,参数为是一个数组,一个记忆方式为,C对应comma,A对应Array。 call的主要运用场景就是在面向对象值的模拟继承关系。

例:

function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
//继承了SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"

代码中,通过call方法,在SubType中,借用了SuperType中的函数的代码,以此来为完善自己的属性。这就导致SubType中的实例都有了自己的colors属性。 这种方式可以在子类的构造方法调用中输入参数。

function SuperType(name){
this.name= name;
}
function SubType(){//继承了SuperType,并且传递了参数
SuperType.call(this,"zhangsan");
this.age=20;
}
var instance=new SubType();
alert(instance.name) //zhangsan
alert(instance.age) //20

当然,这种方式也存在缺点,方法都在构造函数中定义,并没有真正复用。

function SuperType(){
this.colors = ["red", "blue", "green"];
this.sayHi=function(){console.log("Hi")}
;}
function SubType(){//继承了SuperTypeSuperType.call(this);
}
var instance1 = new SubType();
var instance2 = new SubType();
alert(instance1.sayHi==instance2.sayHi); //false

组合继承原型链和call方式都有缺点,但是把两者取长补短,形成组合继承。其思想非常简单,用原型链方式,对需要共享的原型属性和方法实现继承。再通过call方式借用构造函数来实现无需共享的属性的继承。这样即有了共享的属性,也有了不共享的属性。

function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
    //sayName能够被共享
    SuperType.prototype.sayName = function() {
    alert(this.name);
};
    function SubType(name, age) {
    //继承属性  call方式继承 ,name属性不会被共享
    SuperType.call(this, name);
    this.age = age;
}
    //继承方法 原型链方式继承
    SubType.prototype = new SuperType();
    SubType.prototype.constructor = SubType;
    SubType.prototype.sayAge = function() {
    alert(this.age);
​​​​​​​};
    var instance1 = new SubType("Nicholas", 29);
    instance1.colors.push("black");
    alert(instance1.colors); //"red,blue,green,black"
    instance1.sayName(); //"Nicholas";
    instance1.sayAge(); //29
    var instance2 = new SubType("Greg", 27);
    alert(instance2.colors); //"red,blue,green"
    instance2.sayName(); //"Greg";
    instance2.sayAge(); //27

可以看到,对于需要共享的属性、方法,采用原型链的方式继承,对于不需要的共享的,比如属性,则用call方法实现继承。这种方式是javascript中最常用的继承方式。 原型式继承 这个方式是json格式的发明人Douglas Crockford所创造。 提出了一个object()函数,可以做到这一点。

 function object(o) {
     function F() {}    
     F.prototype = o;    
     return new F();  
  }

这个object()函数,借用了一个临时函数,把传入的对象作为父对象,作为临时函数的prototype属性,并返回这个临时函数的一个实例作为子对象,从而使得子对象与父对象连在一起。

var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
​​​​​​​};
    var anotherPerson = object(person);
    anotherPerson.name = "Greg";
    anotherPerson.friends.push("Rob");
    var yetAnotherPerson = object(person);
    yetAnotherPerson.name = "Linda";
    yetAnotherPerson.friends.push("Barbie");
    alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

这个例子中,person对象作为父对象,把它传入到object函数中,会返回一个新的对象,该对象的原型就是person,所以它的原型中就包含一个基本类型值属性和一个引用类型值属性。

这意味着person.friends 不仅属于person 所有,而且也会被anotherPerson以及yetAnotherPerson 共享。实际上,这就相当于又创建了person 对象的两个副本。

想要了解web开发知识,请查阅 HTML中文网 !!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

javascript面向对象设计 的相关文章

随机推荐

  • 了解JavaScript中的类

    javascript是一种基于原型的语言 xff0c javascript中的每个对象都有一个名为 原型 的隐藏内部属性 xff0c 可用于扩展对象属性和方法 直到最近 xff0c 勤奋的开发人员使用构造函数来模仿JavaScript中面向
  • 深入解析Javascript闭包及实现方法

    什么是闭包和闭包的几种写法和用法 1 什么是闭包 闭包 xff0c 官方对闭包的解释是 xff1a 一个拥有许多变量和绑定了这些变量的环境的表达式 xff08 通常是一个函数 xff09 xff0c 因而这些变量也是该表达式的一部分 闭包的
  • 理解JavaScript中的语法和代码结构

    所有编程语言都必须遵守特定的规则才能运行 确定编程语言的正确结构的这组规则称为语法 许多编程语言主要由具有语法变化的类似概念组成 在本教程中 xff0c 我们将介绍JavaScript语法和代码结构的许多规则和约定 功能性和可读性 在开始使
  • 深入CSS,让网页开发少点“坑”

    通常我们在学习CSS的时候 xff0c 感觉语法很容易掌握 xff0c 实际应用中却碰到各式各样难以填补的 坑 xff0c 为避免大家受到同样的困惑与不解 xff0c 本文详细讲解了CSS中优先级和Stacking Context等高级特性
  • 如何巧用 canvas(画布)为图片加水印?

    在前端中 xff0c 如何巧用 canvas xff08 画布 xff09 为图片加水印 xff1f 下面本篇文章就来给大家介绍一下使用 canvas 给图片添加水印的方法 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c
  • EKF(扩展卡尔曼滤波)参数理解

    EKF VELNE NOISE xff1a 速度在坐标轴 NE 方向上的噪声 通常是在 0 上下波动 设置方法 xff1a 将飞机上电静置若干分钟 xff08 时间尽量长一点 xff09 记录飞 行日志 xff0c 由 EKF3 的 IVN
  • 详解HTML文档声明(DOCTYPE)

    HTML文档通常以类型声明开始 xff0c 该声明将帮助浏览器确定其尝试解析和显示的HTML文档类型 本文将详细介绍文档声明DOCTYPE 特点 文档声明必须是HTML文档的第一行 且顶格显示 xff0c 对大小写不敏感 因为任何放在DOC
  • CSS 不定宽高的垂直水平居中(9种方法)

    垂直居中 xff0c 在 CSS 中是一个老生常谈的问题 xff0c 面试的时候也会时常被提及 所以 xff0c 今天我们就来聊聊 9 种不同的居中方法 有常见的 flex transform absolute 等等 也有 CSS3 的网格
  • html5不常用标签应用场景

    作为一个前端开发 xff0c 在浏览别人家的页面时总是会习惯性的查看他们页面的源码 xff0c 发现大多数网站的页面 xff0c 包括我自己写的页面中用到的最多的布局元素无外乎就是div p span ul dl ol li dt dd s
  • HTML的16个全局属性介绍

    在HTML中 xff0c 属性能表达相当丰富的语义 xff0c 而且属性也会额外提供很多实用的功能 xff0c HTML共支持16个常见的全局属性 HTML原有属性 accesskey 作用 xff1a 浏览器用来创建激活或聚焦元素的快捷键
  • 聊聊JavaScript作用域

    几乎所有的语言都有作用域的概念 xff0c 简单的说 xff0c 作用域就是变量和函数的可访问范围 xff0c 即作用域控制在变量和函数的可见性和生命周期 变量作用域 是程序源代码中定义这个变量的区域 1 全局变量拥有全局作用域 xff0c
  • CSS布局方案大全【整理】

    我们在日常开发中经常遇到布局问题 xff0c 下面罗列几种常用的css布局方案话不多说 xff0c 上代码 xff01 居中布局 以下居中布局均以不定宽为前提 xff0c 定宽情况包含其中 1 水平居中 a inline block 43
  • 你必须知道的一些HTML优化技巧

    如何提升Web页面的性能 xff0c 很多开发人员从多个方面来下手如JavaScript 图像优化 服务器配置 xff0c 文件压缩或是调整CSS 很显然HTML 已经达到了一个瓶颈 xff0c 尽管它是开发Web 界面必备的核心语言 HT
  • 常见的CSS图形绘制合集

    展示40多个常见的纯CSS绘制图形 xff0c 效果实时 xff0c 含源代码 xff0c 想要什么效果直接复制粘贴就好了 就算项目用不到 xff0c 看看别人代码怎么写的也有助于提高CSS的基本功 以下这些造型简单的图形都是纯CSS外加一
  • 扩展Vue.js组件

    您的Vue应用程序中是否有共享类似选项的组件 xff0c 甚至模板标记 使用公共选项和标记创建基本组件 xff0c 然后扩展基本组件以创建子组件 xff0c 这是一个好主意 这样的体系结构将帮助您在代码中应用DRY原则 不要重复您自己 xf
  • Node.js日志记录指南

    当你开始用 JavaScript 进行开发时 xff0c 可能学到的第一件事就是如何用 console log 将内容记录到控制台 如果你去搜索如何调试 JavaScript xff0c 会发现数百篇博文和 StackOverflow 文章
  • 软件测试的测试方法及测试流程

    一 测试方法 xff1a 白盒测试 xff1a 把软件比作一个打开的盒子 xff0c 可以看到软件代码的实现 xff0c 针对代码的实现验证代码是否存在问题 单元测试阶段采用的测试方法 灰盒测试 xff1a 介于白盒和黑盒测试之间 灰盒测试
  • 18个用于创建漂亮图表的JavaScript库推荐

    几乎不可能想象没有图形和图表的任何仪表盘 它们快速有效地呈现复杂的统计数据 此外 xff0c 一个好的图表还可以增强网站的整体设计 在本文中 xff0c 我将向您展示一些用于图形和图表的最佳JavaScript库 这些库将帮助您为将来的项目
  • JavaScript基础--引用数据类型 (对象)

    本文主要讲述了JavaScript对象的属性和对象的引用 xff0c 以及对象的读取 赋值 删除和获取对象键名的操作 1 对象 对象就是一组 键值对 xff08 key value xff09 的集合 xff0c 是一种无序的复合数据集合
  • javascript面向对象设计

    javascript和java语言不一样 xff0c 它没有类这个说法 xff0c 更没有子类父类一说 xff0c 所以它所谓的继承机制 xff0c 也是和其他语言完全不同的 创建对象三种方式 1 最简单的方式 xff0c 创建一个obje