Home  >  Article  >  Web Front-end  >  A detailed introduction to object-oriented programming in JavaScript

A detailed introduction to object-oriented programming in JavaScript

黄舟
黄舟Original
2017-03-04 15:54:00947browse

Introduction

JavaScript is a powerful object-oriented programming language, but unlike traditional programming languages, it uses a prototype-based OOP model, making its The syntax is incomprehensible to most developers. In addition, JavaScript also treats functions as primary objects, which may cause greater confusion for developers who are not familiar with the language. That's why we decided to put it up front as a brief introduction, and it can also be used as a reference for object-oriented programming in JavaScript.

This document does not provide a preview of the rules of object-oriented programming, but does provide an overview of their interfaces.

Namespace

With the emergence of more and more third-party libraries, frameworks and web dependencies, namespaces are imperative in the development of JavaScript. We must try to avoid Conflict between global namespace objects and variables.

Unfortunately, JavaScript does not provide support for namespace compilation, but we can use objects to achieve the same result. In JavaScript we have many patterns for implementing namespace interfaces, but we cover nested namespaces, which is the most commonly used pattern in this field.

Nested namespace

The nested namespace pattern uses object literals to bundle functionality with a specific name for a specific application.

We initially create a global object and assign it to a variable called MyApp.

// global namespace
var MyApp = MyApp || {};

The above syntax will check whether MyApp has been defined. If it was already defined, we simply assign it to ourselves, but instead we create an empty container to hold our functions and variables.

We can also use the same technique to create sub-namespaces. For example:

// sub namespaces
MyApp.users = MyApp.user || {};

Once we start our container, we can define our functions and variables inside (the container) and call them in the global namespace without risking conflicts with existing definitions.

// declarations

MyApp.users = {

	existingUsers: '', // variable in namespace

	renderUsersHTML: function() { // function in namespace

		// render html list of users

	}

};

// syntax for using functions within our namespace from the global scope

MyApp.users.renderUsersHTML();

An internal overview of naming patterns in JavaScript was introduced by Addy Osmani of Goggle in the article Essential JavaScript Namespacing Patterns. If you want to explore different modes, this would be a great place to start.

Objects

If you’ve ever written JavaScript code, you’ve already used objects. JavaScript has three types of objects:

Native objects

Native objects are part of the language specification. No matter what running environment they are run in, native objects are available. Native objects include: Array, Date, Math, parseInt, etc. To learn about all native objects, see JavaScript Built-in Object Reference

var cars = Array(); // Array is a native object

Host Objects

Unlike native objects, host objects are created by the environment in which JavaScript code runs. Different environments create different host objects. These host objects allow us to interact with them in most cases. If we write code that runs on a browser (which is one of the running environments), there will be host objects such as window, document, location, and history.

document.body.innerHTML = 'Hello World!'; // document is a host object

// the document object will not be available in a 
// stand-alone environments such as Node.js

User Object

User objects (or implanted objects) are objects defined in our code and created during runtime. There are two ways to create your own objects in JavaScript, detailed below.

Object literal

When we demonstrated the creation of a namespace earlier, we have already come into contact with object literals. Now let’s clarify the definition of an object literal: an object literal is a comma-separated list of name-value pairs enclosed in a pair of curly braces. Object literals can have variables (properties) and functions (methods). Like other objects in JavaScript, it can be used as a function parameter or as a return value.

Now define an object literal and assign it to a variable:

// declaring an object literal

var dog = {

	// object literal definition comes here...

};

Add properties and methods to this object literal, and then access it in the global scope:

// declaring an object literal

var dog = {

	breed: 'Bulldog', // object literal property

	bark: function() { // object literal method

		console.log("Woof!");

	},

};

// using the object

console.log( dog.breed ); // output Bulldog

dog.bark(); // output Woof!

Look here It looks very similar to the previous namespace, but this is no coincidence. The most typical use of literal objects is to encapsulate code in an encapsulated package to avoid conflicts with variables or objects in the global scope. It is also often used to pass configuration parameters to plugins or objects for similar reasons.

If you are familiar with Design Patterns, object literals are singletons to a certain extent, which is the pattern with only one instance. Object literals inherently do not have the ability to instantiate and inherit. Next, we have to learn about another way to create custom objects in JavaScript.

Constructor

Definition of constructor

The function is a first-class citizen of JavaScript, which means that all operation functions supported by other entities are supported. In the world of JavaScript, functions can be dynamically constructed at runtime, can be used as parameters, can be used as return values ​​​​of other functions, and can also be assigned to variables. Moreover, functions can also have their own properties and methods. The nature of functions in JavaScript makes them something that can be instantiated and inherited.

Let’s see how to use the constructor to create a custom object:

// creating a function

function Person( name, email ) {

	// declaring properties and methods using the (this) keyword

	this.name 	= name;
	this.email 	= email;

	this.sayHey = function() {

		console.log( "Hey, I’m " + this.name );

	};

}

// instantiating an object using the (new) keyword

var steve = new Person( "Steve", "steve@hotmail.com" );

// accessing methods and properties

steve.sayHey();

创建构造函数类似于创建普通函数,只有一点例外:用 this 关键字定义自发性和方法。一旦函数被创建,就可以用 new 关键字来生成实例并赋予变量。每次使用 new 关键字,this 都指向一个新的实例。

构建函数实例化和传统面向对象编程语言中的通过类实例化并非完全不同,但是,这里存在一个可能不易被察觉的问题。

当使用 new 关键字创建新对象的时候,函数块会被反复执行,这使得每次运行都会产生新的匿名函数来定义方法。这就像创建新的对象一样,会导致程序消耗更多内存。这个问题在现代浏览器上运行的程序中并不显眼。但随着应用规则地扩大,在旧一点的浏览器、计算机或者低电耗设备中就会出现性能问题。不过不用担心,有更好的办法将方法附加给构造函数(是不会污染全局环境的哦)。

方法和原型

前面介绍中提到 JavaScript 是一种基于原型的编程语言。在 JavaScript 中,可以把原型当作对象模板一样来使用。原型能避免在实例化对象时创建多余的匿名函数和变量。

在 JavaScript 中,prototype 是一个非常特别的属性,可以让我们为对象添加新的属性和方法。现在用原型重写上面的示例看看:

// creating a function

function Person( name, email ) {

	// declaring properties and methods using the (this) keyword

	this.name 	= name;
	this.email 	= email;

}

// assign a new method to the object’s prototype

Person.prototype.sayHey = function() {

	console.log( "Hey, I’m " + this.name );

}

// instantiating a new object using the constructor function

var steve = new Person( "Steve", "steve@hotmail.com" );

// accessing methods and properties

steve.sayHey();

这个示例中,不再为每个 Person 实例定义 sayHey 方法,而是通过原型模板在各实例中共享这个方法。

继承性

通过原型链,原型可以用来实例继承。JavaScript 的每一个对象都有原型,而原型是另外一个对象,也有它自己的原型,周而复始…直到某个原型对象的原型是 null——原型链到此为止。

在访问一个方法或属性的时候,JavaScript 首先检查它们是否在对象中定义,如果不,则检查是否定义在原型中。如果在原型中也没找到,则会延着原型链一直找下去,直到找到,或者到达原型链的终端。

现在来看看代码是怎么实现的。可以从上一个示例中的 Person 对象开始,另外再创建一个叫 Employee 的对象。

// Our person object

function Person( name, email ) {

	this.name 	= name;
	this.email 	= email;

}

Person.prototype.sayHey = function() {

	console.log( "Hey, I’m " + this.name );

}

// A new employee object

function Employee( jobTitle ) {

	this.jobTitle = jobTitle;

}

现在 Employee 只有一个属性。不过既然员工也属于人,我们希望它能从 Person 继承其它属性。要达到这个目的,我们可以在 Employee 对象中调用 Person 的构造函数,并配置原型链。

// Our person object

function Person( name, email ) {

	this.name 	= name;
	this.email 	= email;

}

Person.prototype.sayHey = function() {

	console.log( "Hey, I’m " + this.name );

}

// A new employee object

function Employee( name, email, jobTitle ) {

	// The call function is calling the Constructor of Person
	// and decorates Employee with the same properties

	Person.call( this, name, email );

	this.jobTitle = jobTitle;

}

// To set up the prototype chain, we create a new object using 
// the Person prototype and assign it to the Employee prototype

Employee.prototype = Object.create( Person.prototype );

// Now we can access Person properties and methods through the
// Employee object

var matthew = new Employee( "Matthew", "matthew@hotmail.com", "Developer" );

matthew.sayHey();

要适应原型继承还需要一些时间,但是这一个必须熟悉的重要概念。虽然原型继承模型常常被认为是 JavaScript 的弱点,但实际上它比传统模型更强大。比如说,在掌握了原型模型之后创建传统模型简直就太容易了。

ECMAScript 6 引入了一组新的关键字用于实现 类。虽然新的设计看起来与传统基于类的开发语言非常接近,但它们并不相同。JavaScript 仍然基于原型。

以上就是JavaScript 中的面向对象编程的详细介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn