多学习前端开发的小伙伴都会遇到这样的问题:在JavaScript中全局变量经常会引起命名冲突,甚至有时侯重写变量不是按照想像中的顺序来。避免全局变量名冲突的最好办法还是创建命名空间,接下来千锋广州Web培训老师就给大家分享在JavaScript中创建命名空间的几种常用方法。
JavaScript的执行环境由各种各样的全局变量构成,这些全局变量先于函数执行环境而创建、这些全局变量都挂载于“全局对象”下。当名称有冲突时就会产生一些不可控的问题,比如命名冲突、代码的脆弱性、难以测试等。
?在编程开发中合理的使用命名空间可以避免相同的变量或对象名称产生的冲突,而且命名空间也有助于组织代码有更强的可维护性和可读性。JavaScript中虽然没有提供原生的命名空间支持,但我们可以使用其他的方法(对象和闭包)实现类似的效果。
1、单一全局变量
JavaScript中一个流行的命名空间模式是选择一个全局变量作为主要的引用对象,因为每个可能的全局变量都成为唯一全局变量的属性,也就不用再创建多个全局变量,也就避免了和其他声明的冲突。不过单一全局变量模式已经在不少的JavaScript类库中使用,如:YUI定义了唯一的YUI全局对象,jQuery定义了$和jQuery,$由其他类库使用时使用jQuery等。示例如下:
var myApplication=(function() {
function() {
// ***
},
return {
// **
}
})();
2、命名空间前缀
命名空间前缀模式其思路非常清晰,就是选择一个独特的命名空间,然后在其后面声明声明变量、方法和对象。示例如下:
var=myApplication_propertyA={};
var=myApplication_propertyA={};
function myApplication_myMethod() {
// ***
}
从某种程度上来说,它确实减少了命名冲突的发生概率,但其并没有减少全局变量的数目,在使用这种模式时一定要特别注意。
3、对象字面量表示法
对象字面量模式可以认为是包含一组键值对的对象,每一对键和值由冒号分隔,键也可以是代码新的命名空间。示例如下:
var myApplication={
// 可以很容易的为对象字面量定义功能
getInfo:function() {
// ***
},
// 可以进一步支撑对象命名空间
models:{},
views:{
pages:{}
},
collections:{}
};
对象字面量为我们提供了优雅的键/值语法,我们可以非常便捷的组织代码封装不同的逻辑或功能,而且可读性、可维护性、可扩展性极强。
4、嵌套命名空间
嵌套命名空间模式可以说是对象字面量模式的升级版、它也是一种有效的避免冲突模式、因为即使一个命名空间存在、它也不太可能拥有同样的嵌套子对象、示例如下:
var myApplication=myApplication || {};
// 定义嵌套子对象
myApplication.routers=myApplication.routers || {};
myApplication.routers.test=myApplication.routers.test || {};
当然、我们也可以选择声明新的嵌套命名空间或属性作为索引属性、如:
myApplication[′routers′]=myApplication[′routers′] || {};
使用嵌套命名空间模式可以使代码易读且有组织性,而且相对安全、不易产生冲突,其弱点是如果我们的命名空间嵌套过多会增加浏览器的查询工作量。
5、立即调用的函数表达式
立即调用函数(IIFE)实际上就是匿名函数,被定义后立即被调用。IIFE是用于封装应用程序逻辑的常用方法,以保护它免受全局名称空间的影响。示例如下:
// 命名空间和undefined作为参数传递,确保:
// 1.命名空间可以在局部修改,不重写函数外部上下文
// 2.undefined 的参数值是确保undefined,避免ES5规范里定义的undefined
(function (namespace, undefined) {
// 私有属性
var foo="foo";
bar="bar";
// 公有方法和属性
namespace.foobar="foobar";
namespace.sayHello=function () {
say("Hello World!");
};
// 私有方法
function say(str) {
console.log("You said:" str);
};
})(window.namespace=window.namespace || {});
可扩展性是任何可伸缩命名空间模式的关键,使用IIFE可以轻松实现这一目的,我们可以再次使用IIFE给命名空间添加更多的功能。
6、命名空间注入
命名空间注入是IIFE的另一个变体,从函数包装器内部为一个特定的命名空间“注入”方法和属性,使用this作为命名空间代理,这种模式的优点是可以将功能行为应用到多个对象或命名空间。示例如下:
var myApplication=myApplication || {};
myApplication.utils={};
(function () {
var value=5;
this.getValue=function () {
return value;
}
// 定义新的子命名空间
this.tools={};
}).apply(myApplication.utils);
(function () {
this.diagnose=function () {
return "diagnose";
}
}).apply(myApplication.utils.tools);
// 同样的方式在普通的IIFE上扩展功能,仅仅将上下文作为参数传递并修改,而不是仅仅使用this,如果你经常被全局变量冲突困扰,一定要牢记JavaScript命名空间知识点。
ML 命名空间提供避免元素命名冲突的方法。
命名冲突
在 XML 中,元素名称是由开发者定义的,当两个不同的文档使用相同的元素名时,就会发生命名冲突。
这个 XML 携带 HTML 表格的信息:
<table>
<tr>
<td>Apples</td>
<td>Bananas</td>
</tr>
</table>
这个 XML 文档携带有关桌子的信息(一件家具):
<table>
<name>African Coffee Table</name>
<width>80</width>
<length>120</length>
</table>
假如这两个 XML 文档被一起使用,由于两个文档都包含带有不同内容和定义的 <table> 元素,就会发生命名冲突。
XML 解析器无法确定如何处理这类冲突。
使用前缀来避免命名冲突
在 XML 中的命名冲突可以通过使用名称前缀从而容易地避免。
该 XML 携带某个 HTML 表格和某件家具的信息:
<h:table>
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
<f:table>
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
在上面的实例中,不会有冲突,因为两个 <table> 元素有不同的名称。
XML 命名空间 - xmlns 属性
当在 XML 中使用前缀时,一个所谓的用于前缀的命名空间必须被定义。
命名空间是在元素的开始标签的 xmlns 属性中定义的。
命名空间声明的语法如下。xmlns:前缀="URI"。
<root>
<h:table xmlns:h="http://www.w3.org/TR/html4/">
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
<f:table xmlns:f="http://www.w3cschool.cc/furniture">
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
</root>
在上面的实例中,<table> 标签的 xmlns 属性定义了 h: 和 f: 前缀的合格命名空间。
当命名空间被定义在元素的开始标签中时,所有带有相同前缀的子元素都会与同一个命名空间相关联。
命名空间,可以在他们被使用的元素中或者在 XML 根元素中声明:
<root xmlns:h="http://www.w3.org/TR/html4/"
xmlns:f="http://www.w3cschool.cc/furniture">
<h:table>
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
<f:table>
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
</root>
注释:命名空间 URI 不会被解析器用于查找信息。
其目的是赋予命名空间一个惟一的名称。不过,很多公司常常会作为指针来使用命名空间指向实际存在的网页,这个网页包含关于命名空间的信息。
请访问 http://www.w3.org/TR/html4/。
统一资源标识符(URI,全称 Uniform Resource Identifier)
统一资源标识符(URI)是一串可以标识因特网资源的字符。
最常用的 URI 是用来标识因特网域名地址的统一资源定位器(URL)。另一个不那么常用的 URI 是统一资源命名(URN)。
在我们的实例中,我们仅使用 URL。
默认的命名空间
为元素定义默认的命名空间可以让我们省去在所有的子元素中使用前缀的工作。它的语法如下:
xmlns="namespaceURI"
这个 XML 携带 HTML 表格的信息:
<table xmlns="http://www.w3.org/TR/html4/">
<tr>
<td>Apples</td>
<td>Bananas</td>
</tr>
</table>
这个XML携带有关一件家具的信息:
<table xmlns="http://www.w3schools.com/furniture">
<name>African Coffee Table</name>
<width>80</width>
<length>120</length>
</table>
实际使用中的命名空间
XSLT 是一种用于把 XML 文档转换为其他格式的 XML 语言,比如 HTML。
在下面的 XSLT 文档中,您可以看到,大多数的标签是 HTML 标签。
非 HTML 的标签都有前缀 xsl,并由此命名空间标识:xmlns:xsl="http://www.w3.org/1999/XSL/Transform":
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr>
<th align="left">Title</th>
<th align="left">Artist</th>
</tr>
<xsl:for-each select="catalog/cd">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="artist"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
如您还有不明白的可以在下面与我留言或是与我探讨QQ群308855039,我们一起飞!
PHP 命名空间(namespace)是在PHP 5.3中加入的,如果你学过C#和Java,那命名空间就不算什么新事物。 不过在PHP当中还是有着相当重要的意义。
PHP 命名空间可以解决以下两类问题:
定义命名空间
默认情况下,所有常量、类和函数名都放在全局空间下,就和PHP支持命名空间之前一样。
命名空间通过关键字namespace 来声明。如果一个文件中包含命名空间,它必须在其它所有代码之前声明命名空间。语法格式如下;
<?php // 定义代码在 'MyProject' 命名空间中 namespace MyProject; // ... 代码 ...
你也可以在同一个文件中定义不同的命名空间代码,如:
<?php namespace MyProject; const CONNECT_OK=1; class Connection { /* ... */ } function connect() { /* ... */ } namespace AnotherProject; const CONNECT_OK=1; class Connection { /* ... */ } function connect() { /* ... */ } ?>
不建议使用这种语法在单个文件中定义多个命名空间。建议使用下面的大括号形式的语法。
<?php namespace MyProject { const CONNECT_OK=1; class Connection { /* ... */ } function connect() { /* ... */ } } namespace AnotherProject { const CONNECT_OK=1; class Connection { /* ... */ } function connect() { /* ... */ } } ?>
将全局的非命名空间中的代码与命名空间中的代码组合在一起,只能使用大括号形式的语法。全局代码必须用一个不带名称的 namespace 语句加上大括号括起来,例如:
<?php namespace MyProject { const CONNECT_OK=1; class Connection { /* ... */ } function connect() { /* ... */ } } namespace { // 全局代码 session_start(); $a=MyProject\connect(); echo MyProject\Connection::start(); } ?>
在声明命名空间之前唯一合法的代码是用于定义源文件编码方式的 declare 语句。所有非 PHP 代码包括空白符都不能出现在命名空间的声明之前。
<?php declare(encoding='UTF-8'); //定义多个命名空间和不包含在命名空间中的代码 namespace MyProject { const CONNECT_OK=1; class Connection { /* ... */ } function connect() { /* ... */ } } namespace { // 全局代码 session_start(); $a=MyProject\connect(); echo MyProject\Connection::start(); } ?>
以下代码会出现语法错误:
<html> <?php namespace MyProject; // 命名空间前出现了“<html>” 会致命错误 - 命名空间必须是程序脚本的第一条语句 ?>
子命名空间
与目录和文件的关系很像,PHP 命名空间也允许指定层次化的命名空间的名称。因此,命名空间的名字可以使用分层次的方式定义:
<?php namespace MyProject\Sub\Level; //声明分层次的单个命名空间 const CONNECT_OK=1; class Connection { /* ... */ } function Connect() { /* ... */ } ?>
上面的例子创建了常量 MyProject\Sub\Level\CONNECT_OK,类 MyProject\Sub\Level\Connection 和函数 MyProject\Sub\Level\Connect。
命名空间使用
PHP 命名空间中的类名可以通过三种方式引用:
下面是一个使用这三种方式的实例:
file1.php 文件代码
<?php namespace Foo\Bar\subnamespace; const FOO=1; function foo() {} class foo { static function staticmethod() {} } ?>
file2.php 文件代码
<?php namespace Foo\Bar; include 'file1.php'; const FOO=2; function foo() {} class foo { static function staticmethod() {} } /* 非限定名称 */ foo(); // 解析为函数 Foo\Bar\foo foo::staticmethod(); // 解析为类 Foo\Bar\foo ,方法为 staticmethod echo FOO; // 解析为常量 Foo\Bar\FOO /* 限定名称 */ subnamespace\foo(); // 解析为函数 Foo\Bar\subnamespace\foo subnamespace\foo::staticmethod(); // 解析为类 Foo\Bar\subnamespace\foo, // 以及类的方法 staticmethod echo subnamespace\FOO; // 解析为常量 Foo\Bar\subnamespace\FOO /* 完全限定名称 */ \Foo\Bar\foo(); // 解析为函数 Foo\Bar\foo \Foo\Bar\foo::staticmethod(); // 解析为类 Foo\Bar\foo, 以及类的方法 staticmethod echo \Foo\Bar\FOO; // 解析为常量 Foo\Bar\FOO ?>
注意访问任意全局类、函数或常量,都可以使用完全限定名称,例如 \strlen() 或 \Exception 或 \INI_ALL。
在命名空间内部访问全局类、函数和常量:
<?php namespace Foo; function strlen() {} const INI_ALL=3; class Exception {} $a=\strlen('hi'); // 调用全局函数strlen $b=\INI_ALL; // 访问全局常量 INI_ALL $c=new \Exception('error'); // 实例化全局类 Exception ?>
命名空间和动态语言特征
PHP 命名空间的实现受到其语言自身的动态特征的影响。因此,如果要将下面的代码转换到命名空间中,动态访问元素。
example1.php 文件代码:
<?php class classname { function __construct() { echo __METHOD__,"\n"; } } function funcname() { echo __FUNCTION__,"\n"; } const constname="global"; $a='classname'; $obj=new $a; // prints classname::__construct $b='funcname'; $b(); // prints funcname echo constant('constname'), "\n"; // prints global ?>
必须使用完全限定名称(包括命名空间前缀的类名称)。注意因为在动态的类名称、函数名称或常量名称中,限定名称和完全限定名称没有区别,因此其前导的反斜杠是不必要的。
动态访问命名空间的元素
<?php namespace namespacename; class classname { function __construct() { echo __METHOD__,"\n"; } } function funcname() { echo __FUNCTION__,"\n"; } const constname="namespaced"; include 'example1.php'; $a='classname'; $obj=new $a; // 输出 classname::__construct $b='funcname'; $b(); // 输出函数名 echo constant('constname'), "\n"; // 输出 global /* 如果使用双引号,使用方法为 "\\namespacename\\classname"*/ $a='\namespacename\classname'; $obj=new $a; // 输出 namespacename\classname::__construct $a='namespacename\classname'; $obj=new $a; // 输出 namespacename\classname::__construct $b='namespacename\funcname'; $b(); // 输出 namespacename\funcname $b='\namespacename\funcname'; $b(); // 输出 namespacename\funcname echo constant('\namespacename\constname'), "\n"; // 输出 namespaced echo constant('namespacename\constname'), "\n"; // 输出 namespaced ?>
namespace关键字和__NAMESPACE__常量
PHP支持两种抽象的访问当前命名空间内部元素的方法,__NAMESPACE__ 魔术常量和namespace关键字。
常量__NAMESPACE__的值是包含当前命名空间名称的字符串。在全局的,不包括在任何命名空间中的代码,它包含一个空的字符串。
__NAMESPACE__ 示例, 在命名空间中的代码
<?php namespace MyProject; echo '"', __NAMESPACE__, '"'; // 输出 "MyProject" ?>
__NAMESPACE__ 示例,全局代码
<?php echo '"', __NAMESPACE__, '"'; // 输出 "" ?>
常量 __NAMESPACE__ 在动态创建名称时很有用,例如:
使用__NAMESPACE__动态创建名称
<?php namespace MyProject; function get($classname) { $a=__NAMESPACE__ . '\\' . $classname; return new $a; } ?>
关键字 namespace 可用来显式访问当前命名空间或子命名空间中的元素。它等价于类中的 self 操作符。
namespace操作符,命名空间中的代码
<?php namespace MyProject; use blah\blah as mine; // see "Using namespaces: importing/aliasing" blah\mine(); // calls function blah\blah\mine() namespace\blah\mine(); // calls function MyProject\blah\mine() namespace\func(); // calls function MyProject\func() namespace\sub\func(); // calls function MyProject\sub\func() namespace\cname::method(); // calls static method "method" of class MyProject\cname $a=new namespace\sub\cname(); // instantiates object of class MyProject\sub\cname $b=namespace\CONSTANT; // assigns value of constant MyProject\CONSTANT to $b ?>
namespace操作符, 全局代码
<?php namespace\func(); // calls function func() namespace\sub\func(); // calls function sub\func() namespace\cname::method(); // calls static method "method" of class cname $a=new namespace\sub\cname(); // instantiates object of class sub\cname $b=namespace\CONSTANT; // assigns value of constant CONSTANT to $b ?>
使用命名空间:别名/导入
PHP 命名空间支持 有两种使用别名或导入方式:为类名称使用别名,或为命名空间名称使用别名。
在PHP中,别名是通过操作符 use 来实现的. 下面是一个使用所有可能的三种导入方式的例子:
1、使用use操作符导入/使用别名
<?php namespace foo; use My\Full\Classname as Another; // 下面的例子与 use My\Full\NSname as NSname 相同 use My\Full\NSname; // 导入一个全局类 use \ArrayObject; $obj=new namespace\Another; // 实例化 foo\Another 对象 $obj=new Another; // 实例化 My\Full\Classname 对象 NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func $a=new ArrayObject(array(1)); // 实例化 ArrayObject 对象 // 如果不使用 "use \ArrayObject" ,则实例化一个 foo\ArrayObject 对象 ?>
2、 一行中包含多个use语句
<?php use My\Full\Classname as Another, My\Full\NSname; $obj=new Another; // 实例化 My\Full\Classname 对象 NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func ?>
导入操作是在编译执行的,但动态的类名称、函数名称或常量名称则不是。
3、导入和动态名称
<?php use My\Full\Classname as Another, My\Full\NSname; $obj=new Another; // 实例化一个 My\Full\Classname 对象 $a='Another'; $obj=new $a; // 实际化一个 Another 对象 ?>
另外,导入操作只影响非限定名称和限定名称。完全限定名称由于是确定的,故不受导入的影响。
4、导入和完全限定名称
<?php use My\Full\Classname as Another, My\Full\NSname; $obj=new Another; // 实例化 My\Full\Classname 类 $obj=new \Another; // 实例化 Another 类 $obj=new Another\thing; // 实例化 My\Full\Classname\thing 类 $obj=new \Another\thing; // 实例化 Another\thing 类 ?>
使用命名空间:后备全局函数/常量
在一个命名空间中,当 PHP 遇到一个非限定的类、函数或常量名称时,它使用不同的优先策略来解析该名称。类名称总是解析到当前命名空间中的名称。因此在访问系统内部或不包含在命名空间中的类名称时,必须使用完全限定名称,例如:
1、在命名空间中访问全局类
<?php namespace A\B\C; class Exception extends \Exception {} $a=new Exception('hi'); // $a 是类 A\B\C\Exception 的一个对象 $b=new \Exception('hi'); // $b 是类 Exception 的一个对象 $c=new ArrayObject; // 致命错误, 找不到 A\B\C\ArrayObject 类 ?>
对于函数和常量来说,如果当前命名空间中不存在该函数或常量,PHP 会退而使用全局空间中的函数或常量。
2、 命名空间中后备的全局函数/常量
<?php namespace A\B\C; const E_ERROR=45; function strlen($str) { return \strlen($str) - 1; } echo E_ERROR, "\n"; // 输出 "45" echo INI_ALL, "\n"; // 输出 "7" - 使用全局常量 INI_ALL echo strlen('hi'), "\n"; // 输出 "2" if (is_array('hi')) { // 输出 "is not array" echo "is array\n"; } else { echo "is not array\n"; } ?>
全局空间
如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间,与 PHP 引入命名空间概念前一样。在名称前加上前缀 \ 表示该名称是全局空间中的名称,即使该名称位于其它的命名空间中时也是如此。
使用全局空间说明
<?php namespace A\B\C; /* 这个函数是 A\B\C\fopen */ function fopen() { /* ... */ $f=\fopen(...); // 调用全局的fopen函数 return $f; } ?>
命名空间的顺序
自从有了命名空间之后,最容易出错的该是使用类的时候,这个类的寻找路径是什么样的了。
<?php namespace A; use B\D, C\E as F; // 函数调用 foo(); // 首先尝试调用定义在命名空间"A"中的函数foo() // 再尝试调用全局函数 "foo" \foo(); // 调用全局空间函数 "foo" my\foo(); // 调用定义在命名空间"A\my"中函数 "foo" F(); // 首先尝试调用定义在命名空间"A"中的函数 "F" // 再尝试调用全局函数 "F" // 类引用 new B(); // 创建命名空间 "A" 中定义的类 "B" 的一个对象 // 如果未找到,则尝试自动装载类 "A\B" new D(); // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象 // 如果未找到,则尝试自动装载类 "B\D" new F(); // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象 // 如果未找到,则尝试自动装载类 "C\E" new \B(); // 创建定义在全局空间中的类 "B" 的一个对象 // 如果未发现,则尝试自动装载类 "B" new \D(); // 创建定义在全局空间中的类 "D" 的一个对象 // 如果未发现,则尝试自动装载类 "D" new \F(); // 创建定义在全局空间中的类 "F" 的一个对象 // 如果未发现,则尝试自动装载类 "F" // 调用另一个命名空间中的静态方法或命名空间函数 B\foo(); // 调用命名空间 "A\B" 中函数 "foo" B::foo(); // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法 // 如果未找到类 "A\B" ,则尝试自动装载类 "A\B" D::foo(); // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法 // 如果类 "B\D" 未找到,则尝试自动装载类 "B\D" \B\foo(); // 调用命名空间 "B" 中的函数 "foo" \B::foo(); // 调用全局空间中的类 "B" 的 "foo" 方法 // 如果类 "B" 未找到,则尝试自动装载类 "B" // 当前命名空间中的静态方法或函数 A\B::foo(); // 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法 // 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B" \A\B::foo(); // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法 // 如果类 "A\B" 未找到,则尝试自动装载类 "A\B" ?>
名称解析遵循下列规则:
笔记:
可以把非限定名称类比为文件名(例如 comment.php)、.限定名称类比为相对路径名(例如 ./article/comment.php)、完全限定名称类比为绝对路径名(例如 /blog/article/comment.php),这样可能会更容易理解。
再添一例:
*请认真填写需求信息,我们会在24小时内与您取得联系。