第一页 上页 4 5 6 7 8 9 10 11 12 13 下页 最后页 [ 显示模式: 摘要 | 列表 ]

 mysql出现ERROR : (2006, 'MySQL server has gone away') 的问题意思就是指client和MySQL server之间的链接断开了。

造成这样的原因一般是sql操作的时间过长,或者是传送的数据太大(例如使用insert ... values的语句过长, 这种情况可以通过修改max_allowed_packed的配置参数来避免,也可以在程序中将数据分批插入)。

产生这个问题的原因有很多,总结下网上的分析:

原因一. MySQL 服务宕了

判断是否属于这个原因的方法很简单,进入mysql控制台,查看mysql的运行时长

mysql> show global status like 'uptime';
+---------------+---------+
| Variable_name | Value   |
+---------------+---------+
| Uptime        | 3414707 |
+---------------+---------+

1 row in set或者查看MySQL的报错日志,看看有没有重启的信息

如果uptime数值很大,表明mysql服务运行了很久了。说明最近服务没有重启过。
如果日志没有相关信息,也表名mysql服务最近没有重启过,可以继续检查下面几项内容。

原因二. mysql连接超时

即某个mysql长连接很久没有新的请求发起,达到了server端的timeout,被server强行关闭。
此后再通过这个connection发起查询的时候,就会报错server has gone away
(大部分PHP脚本就是属于此类)

mysql> show global variables like '%timeout';
+----------------------------+----------+
| Variable_name              | Value    |
+----------------------------+----------+
| connect_timeout            | 10       |
| delayed_insert_timeout     | 300      |
| innodb_lock_wait_timeout   | 50       |
| innodb_rollback_on_timeout | OFF      |
| interactive_timeout        | 28800    |
| lock_wait_timeout          | 31536000 |
| net_read_timeout           | 30       |
| net_write_timeout          | 60       |
| slave_net_timeout          | 3600     |
| wait_timeout               | 28800    |
+----------------------------+----------+
10 rows in set

wait_timeout 是28800秒,即mysql链接在无操作28800秒后被自动关闭

原因三. mysql请求链接进程被主动kill

这种情况和原因二相似,只是一个是人为一个是MYSQL自己的动作

mysql> show global status like 'com_kill';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_kill      | 21    |
+---------------+-------+
1 row in set原因四. Your SQL statement was too large.

当查询的结果集超过 max_allowed_packet 也会出现这样的报错。定位方法是打出相关报错的语句。

用select * into outfile 的方式导出到文件,查看文件大小是否超过 max_allowed_packet ,如果超过则需要调整参数,或者优化语句。

mysql> show global variables like 'max_allowed_packet';
+--------------------+---------+
| Variable_name      | Value   |
+--------------------+---------+
| max_allowed_packet | 1048576 |
+--------------------+---------+
1 row in set (0.00 sec)

修改参数:

mysql> set global max_allowed_packet=1024*1024*16;
mysql> show global variables like 'max_allowed_packet';
+--------------------+----------+
| Variable_name      | Value    |
+--------------------+----------+
| max_allowed_packet | 16777216 |
+--------------------+----------+
1 row in set (0.00 sec)

以下是补充:

应用程序(比如PHP)长时间的执行批量的MYSQL语句。执行一个SQL,但SQL语句过大或者语句中含有BLOB或者longblob字段。比如,图片数据的处理。都容易引起MySQL server has gone away。 

今天遇到类似的情景,MySQL只是冷冷的说:MySQL server has gone away。 

大概浏览了一下,主要可能是因为以下几种原因: 
一种可能是发送的SQL语句太长,以致超过了max_allowed_packet的大小,如果是这种原因,你只要修改my.cnf,加大max_allowed_packet的值即可。 



还有一种可能是因为某些原因导致超时,比如说程序中获取数据库连接时采用了Singleton的做法,虽然多次连接数据库,但其实使用的都是同一个连接,而且程序中某两次操作数据库的间隔时间超过了wait_timeout(SHOW STATUS能看到此设置),那么就可能出现问题。最简单的处理方式就是把wait_timeout改大,当然你也可以在程序里时不时顺手mysql_ping()一下,这样MySQL就知道它不是一个人在战斗。 

解决MySQL server has gone away 

1、应用程序(比如PHP)长时间的执行批量的MYSQL语句。最常见的就是采集或者新旧数据转化。 
解决方案: 
在my.cnf文件中添加或者修改以下两个变量: 
wait_timeout=2880000 
interactive_timeout = 2880000 
关于两个变量的具体说明可以google或者看官方手册。如果不能修改my.cnf,则可以在连接数据库的时候设置CLIENT_INTERACTIVE,比如: 
sql = "set interactive_timeout=24*3600"; 
mysql_real_query(...) 


2、执行一个SQL,但SQL语句过大或者语句中含有BLOB或者longblob字段。比如,图片数据的处理 
解决方案: 
在my.cnf文件中添加或者修改以下变量: 
max_allowed_packet = 10M(也可以设置自己需要的大小) 
max_allowed_packet 参数的作用是,用来控制其通信缓冲区的最大长度。 


最近做网站有一个站要用到WEB网页采集器功能,当一个PHP脚本在请求URL的时候,可能这个被请求的网页非常慢慢,超过了mysql的 wait-timeout时间,然后当网页内容被抓回来后,准备插入到MySQL的时候,发现MySQL的连接超时关闭了,于是就出现了“MySQL server has gone away”这样的错误提示,解决这个问题,我的经验有以下两点,或许对大家有用处: 
第 一种方法: 
当然是增加你的 wait-timeout值,这个参数是在my.cnf(在Windows下台下面是my.ini)中设置,我的数据库负荷稍微大一点,所以,我设置的值 为10,(这个值的单位是秒,意思是当一个数据库连接在10秒钟内没有任何操作的话,就会强行关闭,我使用的不是永久链接 (mysql_pconnect),用的是mysql_connect,关于这个wait-timeout的效果你可以在MySQL的进程列表中看到 (show processlist) ),你可以把这个wait-timeout设置成更大,比如300秒,呵呵,一般来讲300秒足够用了,其实你也可以不用设置,MySQL默认是8个小 时。情况由你的服务器和站点来定。 
第二种方法: 
这也是我个人认为最好的方法,即检查 MySQL的链接状态,使其重新链接。 
可能大家都知道有mysql_ping这么一个函数,在很多资料中都说这个mysql_ping的 API会检查数据库是否链接,如果是断开的话会尝试重新连接,但在我的测试过程中发现事实并不是这样子的,是有条件的,必须要通过 mysql_options这个C API传递相关参数,让MYSQL有断开自动链接的选项(MySQL默认为不自动连接),但我测试中发现PHP的MySQL的API中并不带这个函数,你重新编辑MySQL吧,呵呵。但mysql_ping这个函数还是终于能用得上的,只是要在其中有一个小小的操作技巧: 
这是我的的数据库操 作类中间的一个函数 

 V8 引擎概览

 
Google V8 引擎使用 C++ 代码编写,实现了 ECMAScript 规范的第五版,可以运行在所有的主流
 
操作系统中,甚至可以运行在移动终端 ( 基于 ARM 的处理器,如 HTC G7 等 )。V8 最早被开发用以嵌入到 Google 的开源浏览器 Chrome 中,但是 V8 是一个可以独立的模块,完全可以嵌入您自己的应用,著名的 Node.js( 一个异步的服务器框架,可以在服务端使用 JavaScript 写出搞笑的网络服务器 ) 就是基于 V8 引擎的。
 
和其他 JavaScript 引擎一样,V8 会编译 / 执行 JavaScript 代码,管理内存,负责垃圾回收,与宿主语言的交互等。V8 的垃圾回收器采用了众多技术,使得其运行效率大大提高。通过暴露宿主对象 ( 变量,函数等 ) 到 JavaScript,JavaScript 可以访问宿主环境中的对象,并在脚本中完成对宿主对象的操作。
 
V8 引擎基本概念
 
图 1. V8 引擎基本概念关系图 ( 根据 Google V8 官方文档 ) 
handle
 
handle 是指向对象的指针,在 V8 中,所有的对象都通过 handle 来引用,handle 主要用于 V8 的垃圾回收机制。
 
在 V8 中,handle 分为两种:持久化 (Persistent)handle 和本地 (Local)handle,持久化 handle 存放在堆上,而本地 handle 存放在栈上。这个与 C/C++ 中的堆和栈的意义相同 ( 简而言之,堆上的空间需要开发人员自己申请,使用完成之后显式的释放;而栈上的为自动变量,在退出函数 / 方法之后自动被释放 )。持久化 handle 与本地 handle 都是 Handle 的子类。在 V8 中,所有数据访问均需要通过 handle。需要注意的是,使用持久化 handle 之后,需要显式的调用 Dispose() 来通知垃圾回收机制。
 
作用域 (scope)
 
scope 是 handle 的集合,可以包含若干个 handle,这样就无需将每个 handle 逐次释放,而是直接释放整个 scope。
 
在使用本地 handle 时,需要声明一个 HandleScope 的实例,scope 是 handle 的容器,使用 scope,则无需依次释放 handle。
 
 HandleScope handle_scope; 
 Local<ObjectTemplate> temp; 
 
上下文 (context)
 
context 是一个执行器环境,使用 context 可以将相互分离的 JavaScript 脚本在同一个 V8 实例中运行,而互不干涉。在运行 JavaScript 脚本是,需要显式的指定 context 对象。
 
数据及模板
 
由于 C++ 原生数据类型与 JavaScript 中数据类型有很大差异,因此 V8 提供了 Data 类,从 JavaScript 到 C++,从 C++ 到 JavaScrpt 都会用到这个类及其子类,比如:
 
 Handle<Value> Add(const Arguments& args){ 
   int a = args[0]->Uint32Value(); 
   int b = args[1]->Uint32Value(); 
 
   return Integer::New(a+b); 
 } 
 
Integer 即为 Data 的一个子类。
 
V8 中,有两个模板 (Template) 类 ( 并非 C++ 中的模板类 ):对象模板 (ObjectTempalte) 和函数模板 (FunctionTemplate),这两个模板类用以定义 JavaScript 对象和 JavaScript 函数。我们在后续的小节部分将会接触到模板类的实例。通过使用 ObjectTemplate,可以将 C++ 中的对象暴露给脚本环境,类似的,FunctionTemplate 用以将 C++ 函数暴露给脚本环境,以供脚本使用。
 
初始化 context 是使用 V8 引擎所必需的过程,代码非常简单:
 
 Persistent<Context> context = Context::New(); 
V8 引擎使用示例
 
有了上面所述的基本概念之后,我们来看一下一个使用 V8 引擎的应用程序的基本流程:
 
创建 HandleScope 实例
创建一个持久化的 Context
进入 Context
创建脚本字符串
创建 Script 对象,通过 Script::Compile()
执行脚本对象的 Run 方法
获取 / 处理结果
显式的调用 Context 的 Dispose 方法
基本代码模板
 
 
清单 1. 代码模块 
        
 #include <v8.h> 
 
 using namespace v8; 
 
 int main(int argc, char *argv[]) { 
   // 创建一个句柄作用域 ( 在栈上 ) 
   HandleScope handle_scope; 
 
   // 创建一个新的上下文对象
   Persistent<Context> context = Context::New(); 
 
   // 进入上一步创建的上下文,用于编译执行 helloworld 
   Context::Scope context_scope(context); 
 
   // 创建一个字符串对象,值为'Hello, Wrold!', 字符串对象被 JS 引擎
   // 求值后,结果为'Hello, World!'
   Handle<String> source = String::New("'Hello' + ', World!'"); 
 
   // 编译字符串对象为脚本对象
   Handle<Script> script = Script::Compile(source); 
 
   // 执行脚本,获取结果
   Handle <Value> result = script->Run(); 
 
   // 释放上下文资源
   context.Dispose(); 
 
   // 转换结果为字符串
   String::AsciiValue ascii(result); 
 
   printf("%s\n", *ascii); 
 
   return 0; 
 } 
 
以上代码为一个使用 V8 引擎来运行脚本的基本模板,可以看到,开发人员可以很容易的在自己的代码中嵌入 V8 来处理 JavaScript 脚本。我们在下面小节中详细讨论如何在脚本中访问 C++ 资源。
 
使用 C++ 变量
 
在 JavaScript 与 V8 间共享变量事实上是非常容易的,基本模板如下:
 
 
清单 2. 共享变量 
        
 static type xxx; 
 
 static Handle<Value> xxxGetter( 
   Local<String> name, 
   const AccessorInfo& info){ 
 
   //code about get xxx 
 } 
 
 static void xxxSetter( 
   Local<String> name, 
   Local<Value> value, 
   const AccessorInfo& info){ 
 
   //code about set xxx 
 } 
 
首先在 C++ 中定义数据,并以约定的方式定义 getter/setter 函数,然后需要将 getter/setter 通过下列机制公开给脚本:
 
 global->SetAccessor(String::New("xxx"), xxxGetter, xxxSetter); 
 
其中,global 对象为一个全局对象的模板:
 
 Handle<ObjectTemplate> global = ObjectTemplate::New(); 
 
下面我们来看一个实例:
 
 
清单 3. 实例 1 
        
 static char sname[512] = {0}; 
 
 static Handle<Value> NameGetter(Local<String> name, 
     const AccessorInfo& info) { 
   return String::New((char*)&sname,strlen((char*)&sname)); 
 } 
 
 static void NameSetter(Local<String> name, 
     Local<Value> value, 
     const AccessorInfo& info) { 
   Local<String> str = value->ToString(); 
   str->WriteAscii((char*)&sname); 
 } 
 
定义了 NameGetter, NameSetter 之后,在 main 函数中,将其注册在 global 上:
 
 // Create a template for the global object. 
 Handle<ObjectTemplate> global = ObjectTemplate::New(); 
 
 //public the name variable to script 
 global->SetAccessor(String::New("name"), NameGetter, NameSetter); 
 
在 C++ 中,将 sname 的值设置为”cpp”:
 
 //set sname to "cpp" in cpp program 
 strncpy(sname, "cpp", sizeof(sname)); 
 
然后在 JavaScript 中访问该变量,并修改:
 
 print(name); 
 
 //set the variable `name` to "js"
 name='js'; 
 print(name); 
 
运行结果如下:
 
 cpp 
 js 
 
运行脚本,第一个 print 调用会打印在 C++ 代码中设置的 name 变量的值:cpp,然后我们在脚本中修改 name 值为:js,再次调用 print 函数则打印出设置后的值:js。
 
调用 C++ 函数
 
在 JavaScript 中调用 C++ 函数是脚本化最常见的方式,通过使用 C++ 函数,可以极大程度的增强 JavaScript 脚本的能力,如文件读写,网络 / 数据库访问,图形 / 图像处理等等,而在 V8 中,调用 C++ 函数也非常的方便。
 
在 C++ 代码中,定义以下原型的函数:
 
 Handle<Value> function(constArguments& args){ 
   //return something 
 } 
 
然后,再将其公开给脚本:
 
 global->Set(String::New("function"),FunctionTemplate::New(function)); 
 
同样,我们来看两个示例:
 
 
清单 4. 实例 2 
        
 Handle<Value> Add(const Arguments& args){ 
   int a = args[0]->Uint32Value(); 
   int b = args[1]->Uint32Value(); 
 
   return Integer::New(a+b); 
 } 
 
 Handle<Value> Print(const Arguments& args) { 
   bool first = true; 
   for (int i = 0; i < args.Length(); i++) { 
     HandleScope handle_scope; 
     if (first) { 
       first = false; 
     } else { 
       printf(" "); 
    
     String::Utf8Value str(args[i]); 
     const char* cstr = ToCString(str); 
     printf("%s", cstr); 
  
   printf("\n"); 
   fflush(stdout); 
   return Undefined(); 
 } 
 
函数 Add 将两个参数相加,并返回和。函数 Print 接受任意多个参数,然后将参数转换为字符串输出,最后输出换行。
 
 global->Set(String::New("print"), FunctionTemplate::New(Print)); 
 global->Set(String::New("add"), FunctionTemplate::New(Add)); 
 
我们定义以下脚本:
 
 var x = (function(a, b){ 
   return a + b;   
 })(12, 7); 
 
 print(x); 
 
 //invoke function add defined in cpp 
 var y = add(43, 9); 
 print(y); 
 
运行结果如下:
 
 19 
 52 
 
使用 C++ 类
 
如果从面向对象的视角来分析,最合理的方式是将 C++ 类公开给 JavaScript,这样可以将 JavaScript 内置的对象数量大大增加,从而尽可能少的使用宿主语言,而更大的利用动态语言的灵活性和扩展性。事实上,C++ 语言概念众多,内容繁复,学习曲线较 JavaScript 远为陡峭。最好的应用场景是:既有脚本语言的灵活性,又有 C/C++ 等系统语言的效率。使用 V8 引擎,可以很方便的将 C++ 类”包装”成可供 JavaScript 使用的资源。
 
我们这里举一个较为简单的例子,定义一个 Person 类,然后将这个类包装并暴露给 JavaScript 脚本,在脚本中新建 Person 类的对象,使用 Person 对象的方法。
 
首先,我们在 C++ 中定义好类 Person:
 
 
清单 5. 定义类 
        
 class Person { 
 private: 
   unsigned int age; 
   char name[512]; 
 
 public: 
   Person(unsigned int age, char *name) { 
     this->age = age; 
     strncpy(this->name, name, sizeof(this->name)); 
  
 
   unsigned int getAge() { 
     return this->age; 
  
 
   void setAge(unsigned int nage) { 
     this->age = nage; 
  
 
   char *getName() { 
     return this->name; 
  
 
   void setName(char *nname) { 
     strncpy(this->name, nname, sizeof(this->name)); 
  
 }; 
 
Person 类的结构很简单,只包含两个字段 age 和 name,并定义了各自的 getter/setter. 然后我们来定义构造器的包装:
 
 Handle<Value> PersonConstructor(const Arguments& args){ 
   Handle<Object> object = args.This(); 
   HandleScope handle_scope; 
   int age = args[0]->Uint32Value(); 
 
   String::Utf8Value str(args[1]); 
   char* name = ToCString(str); 
 
   Person *person = new Person(age, name); 
   object->SetInternalField(0, External::New(person)); 
   return object; 
 } 
 
从函数原型上可以看出,构造器的包装与上一小节中,函数的包装是一致的,因为构造函数在 V8 看来,也是一个函数。需要注意的是,从 args 中获取参数并转换为合适的类型之后,我们根据此参数来调用 Person 类实际的构造函数,并将其设置在 object 的内部字段中。紧接着,我们需要包装 Person 类的 getter/setter:
 
 Handle<Value> PersonGetAge(const Arguments& args){ 
   Local<Object> self = args.Holder(); 
   Local<External> wrap = Local<External>::Cast(self->GetInternalField(0)); 
 
   void *ptr = wrap->Value(); 
 
   return Integer::New(static_cast<Person*>(ptr)->getAge()); 
 } 
 
 Handle<Value> PersonSetAge(const Arguments& args) 
 { 
   Local<Object> self = args.Holder(); 
   Local<External> wrap = Local<External>::Cast(self->GetInternalField(0)); 
 
   void* ptr = wrap->Value(); 
 
   static_cast<Person*>(ptr)->setAge(args[0]->Uint32Value()); 
   return Undefined(); 
 } 
 
而 getName 和 setName 的与上例类似。在对函数包装完成之后,需要将 Person 类暴露给脚本环境:
 
首先,创建一个新的函数模板,将其与字符串”Person”绑定,并放入 global:
 
 Handle<FunctionTemplate> person_template = FunctionTemplate::New(PersonConstructor); 
 person_template->SetClassName(String::New("Person")); 
 global->Set(String::New("Person"), person_template); 
 
然后定义原型模板:
 
 Handle<ObjectTemplate> person_proto = person_template->PrototypeTemplate(); 
 
 person_proto->Set("getAge", FunctionTemplate::New(PersonGetAge)); 
 person_proto->Set("setAge", FunctionTemplate::New(PersonSetAge)); 
 
 person_proto->Set("getName", FunctionTemplate::New(PersonGetName)); 
 person_proto->Set("setName", FunctionTemplate::New(PersonSetName)); 
 
最后设置实例模板:
 
 Handle<ObjectTemplate> person_inst = person_template->InstanceTemplate(); 
 person_inst->SetInternalFieldCount(1); 
 
随后,创建一个用以测试的脚本:
 
 //global function to print out detail info of person 
 function printPerson(person){ 
    print(person.getAge()+":"+person.getName()); 
 } 
 
 //new a person object 
 var person = new Person(26, "juntao"); 
 
 //print it out 
 printPerson(person); 
 
 //set new value 
 person.setAge(28); 
 person.setName("juntao.qiu"); 
 
 //print it out 
 printPerson(person); 
 
运行得到以下结果:
 
 26:juntao 
 28:juntao.qiu 
简单示例
 
在这一小节中,我们将编写一个简单的桌面计算器:表达式求值部分通过 V8 引擎来进行,而流程控制部分则放在 C++ 代码中,这样可以将表达式解析等复杂细节绕开。同时,我们还得到了一个额外的好处,用户在脚本中可以自定义函数,从而可以在计算器中定义自己的运算规则。
 
桌上计算器
 
计算器程序首先进入一个 MainLoop,从标准输入读取一行命令,然后调用 V8 引擎去求值,然后将结果打印到控制台,然后再进入循环:
 
 
清单 6. 桌面计算器示例 
        
 void MainLoop(Handle<Context> context) { 
   while(true) { 
     char buffer[1024] = {0}; 
     printf("$ "); 
     char *str = fgets(buffer, sizeof(buffer), stdin); 
     if(str == NULL) { 
       break; 
    
     HandleScope handle_scope; 
     ExecuteString(String::New(str), String::New("calc"), true); 
  
 } 
 
在 main 函数中设置全局对象,创建上下文对象,并进入 MainLoop:
 
 int main(int argc, char *argv[]){ 
   HandleScope handle_scope; 
 
   // Create a template for the global object. 
   Handle<ObjectTemplate> global = ObjectTemplate::New(); 
 
   // Expose the local functions to script 
   global->Set(String::New("load"), FunctionTemplate::New(Load)); 
   global->Set(String::New("print"), FunctionTemplate::New(Print)); 
   global->Set(String::New("quit"), FunctionTemplate::New(Quit)); 
 
   // Create a new execution environment containing the built-in 
   // functions 
   Handle<Context> context = Context::New(NULL, global); 
 
   // Enter the newly created execution environment. 
   Context::Scope context_scope(context); 
 
   // Enter main loop 
   MainLoop(context); 
 
   V8::Dispose(); 
 
 
   return 0; 
 } 
 
在 main 函数中,为脚本提供了三个函数,load 函数用以将用户指定的脚本加载进来,并放入全局的上下文中一边引用,print 函数用以打印结果,而 quit 提供用户退出计算器的功能。
 
测试一下:
 
 $ 1+2 
 3 
 
 $ (10+3)/(9.0-5) 
 3.25 
 
 $ typeof print 
 function 
 
 $ typeof non 
 undefined 
 
 // 自定义函数
 $ function add(a, b){return a+b;} 
 $ add(999, 2323) 
 3322 
 
 // 查看 print 标识符的内容
 $ print 
 function print() { [native code] } 
 
load 函数提供了用户自定义函数的功能,将脚本文件作为一个字符串加载到内存,然后对该字符串编译,求值,并将处理过的脚本对象放入当前 context 中,以便用户使用。
 
 Handle<Value> Load(const Arguments& args){ 
   if(args.Length() != 1){ 
     return Undefined(); 
  
 
   HandleScope handle_scope; 
   String::Utf8Value file(args[0]); 
 
   Handle<String> source = ReadFile(*file); 
   ExecuteString(source, String::New(*file), false); 
 
   return Undefined(); 
 } 
 
而 ExecuteString 函数,负责将字符串编译运行:
 
 bool ExecuteString(Handle<String> source, 
           Handle<Value> name, 
           bool print_result) 
 { 
   HandleScope handle_scope; 
   TryCatch try_catch; 
   Handle<Script> script = Script::Compile(source, name); 
   if (script.IsEmpty()) { 
     return false; 
   } else { 
     Handle<Value> result = script->Run(); 
     if (result.IsEmpty()) { 
       return false; 
     } else { 
       if (print_result && !result->IsUndefined()) { 
         String::Utf8Value str(result); 
         const char* cstr = ToCString(str); 
         printf("%s\n", cstr); 
      
       return true; 
    
  
 } 
 
将下列内容存入一个文本文件,并命令为 calc.js:
 
 function sum(){ 
   var s = 0; 
   for(var i = 0; i < arguments.length; i++){ 
     s += arguments[i]; 
  
   return s; 
 } 
 
 function avg(){ 
   var args = arguments; 
   var count = args.length; 
   var sum = 0; 
   for(var i = 0; i < count; i++){ 
     sum += args[i]; 
  
   return sum/count; 
 } 
 
然后在计算器中测试:
 
 // 此时 sum 符号位定义
 $ typeof sum 
 undefined 
 
 // 加载文件,并求值
 $ load("calc.js") 
 
 // 可以看到,sum 的类型为函数
 $ typeof sum 
 function 
 
 $ sum(1,2,3,4,5,6,7,8,9) 
 45 
结束语
 
使用 V8 引擎,可以轻松的将脚本的好处带进 C++ 应用,使得 C++ 应用更具灵活性,扩展性。我们在文中讨论了基本的模板,如何使用 C++ 变量,函数,以及类。最后的实例中给出了一个计算器的原型。由于 V8 的设计原则,开发人员可以快速的将其嵌入到自己的应用中,并且无需太过担心脚本语言的执行效率。

设备像素比

[不指定 2017/01/17 10:53 | by 刘新修 ]

 /****** 以iphone为基准||宽度为100则为1倍宽 ******/

var y={}
y.fontSize=100*(document.documentElement.clientWidth/320);
var b = y.fontSize;
b = b >= 240 ? 240 : b;  //最大就240||100就是一倍宽
document.documentElement.style.fontSize = b + "px"

nginx出现502有很多原因,但大部分原因可以归结为资源数量不够用,也就是说后端php-fpm处理有问题,nginx将正确的客户端请求发给了后端的php-fpm进程,但是因为php-fpm进程的问题导致不能正确解析php代码,最终返回给了客户端502错误。

服务器出现502的原因是连接超时 我们向服务器发送请求 由于服务器当前链接太多,导致服务器方面无法给于正常的响应,产生此类报错

因此如果你服务器并发量非常大,那只能先增加机器,然后按以下方式优化会取得更好效果;但如果你并发不大却出现502,一般都可以归结为配置问题,脚本超时问题。

一、php-fpm进程数不够用

使用 netstat -napo |grep "php-fpm" | wc -l 查看一下当前fastcgi进程个数,如果个数接近conf里配置的上限,就需要调高进程数。

但也不能无休止调高,可以根据服务器内存情况,可以把php-fpm子进程数调到100或以上,在4G内存的服务器上200就可以。


二、调高调高linux内核打开文件数量

可以使用这些命令(必须是root帐号)

C#代码
  1. echo 'ulimit -HSn 65536' >> /etc/profile  
  2. echo 'ulimit -HSn 65536' >> /etc/rc.local  
  3. source /etc/profile  

三、脚本执行时间超时

 

如果脚本因为某种原因长时间等待不返回 ,导致新来的请求不能得到处理,可以适当调小如下配置。

nginx.conf里面主要是如下

C#代码
  1. fastcgi_connect_timeout 300;  
  2. fastcgi_send_timeout 300;  
  3. fastcgi_read_timeout 300;  

php-fpm.conf里如要是如下

C#代码
  1. request_terminate_timeout = 10s  

四、缓存设置比较小

 

修改或增加配置到nginx.conf

C#代码
  1. proxy_buffer_size 64k;  
  2. proxy_buffers  512k;  
  3. proxy_busy_buffers_size 128k;  

五、 recv() failed (104: Connection reset by peer) while reading response header from upstream

 

可能的原因机房网络丢包或者机房有硬件防火墙禁止访问该域名

但最重要的是程序里要设置好超时,不要使用php-fpm的request_terminate_timeout,

最好设成request_terminate_timeout=0;

因为这个参数会直接杀掉php进程,然后重启php进程,这样前端nginx就会返回104: Connection reset by peer。这个过程是很慢,总体感觉就是网站很卡。

C#代码
  1. May 01 10:50:58.044162 [WARNING] [pool www] child 4074, script '/usr/local/nginx/html/quancha/sameip/detail.php' execution timed out (15.129933 sec), terminating  
  2. May 01 10:50:58.045725 [WARNING] [pool www] child 4074 exited on signal 15 SIGTERM after 90.227060 seconds from start  
  3. May 01 10:50:58.046818 [NOTICE] [pool www] child 4082 started  

说一千道一万最重要的就是程序里控制好超时,gethostbyname、curl、file_get_contents等函数的都要设置超时时间。

 

另一个就是多说,这个东西是增加了网站的交互性,但是使用的多了反应就慢了,如果你网站超时且使用了多说是,可以关闭它。

php-fpm.conf max_children 和 max_requests配置:

pm = static

假如使用静态 pm.max_children这个参数会起作用,其余不会。动态反之。

2G内存pm.max_children大概开启50左右,按照实际情况来调优,这个是很必要的。

========================================================

max_children是PHP-FPM Pool 最大的子进程数,他数值取决于你的服务器内存。 假设你打算给10G内存给当前配置的PHP-FPM Pool,一般一个PHP请求占用内存10M-40M,我们按站点每个PHP请求占用内存25M,这样max_children = 10G/25M = 409。所以,这个值可以根据情况算出来

max_requests是每个子进程重生之前处理的请求数, 默认值为unlimited(默认为1024),可以设置小一点(如500左右),这样可以避免内存泄露带来的问题

Nginx代理过程,将业务服务器请求数据缓存到本地文件,再将文件数据转发给请求客户端。高并发的客户端请求,必然要求服务器文件句柄的并发打开限制。使用ulimit命令(ulimit -n),查看Linux系统文件句柄并发限制,默认是1024,我们可以改为65535(2 的 16 次方,这是系统端口的极限)。修改的方法为:修改系统文件/etc/security/limits.conf,添加如下信息,并重新启动系统生效。

C#代码
  1. * soft   nofile  65535  
  2.   
  3. * hard   nofile  65535  

然后在Nginx配置文件中,把文件限制及连接数信息改为65535:

C#代码
  1. worker_rlimit_nofile 65535;  
  2. events {  
  3.     use epoll;  
  4.     worker_connections  65535;  
  5. }  

 

NGINX 重写规则

[不指定 2016/11/11 12:06 | by 刘新修 ]

nginx rewrite 实现二级域名跳转

当访问http://abc.test.com跳转到http://www.test.com/test/abc/
方法一:
这种方法浏览器地址会变www.test.com/test/abc
实现访问如下:
server { 
        listen 80; 
        server_name www.test.com; 
        location / { 
                root /data/test; 
                index index.html; 
        } 
 
 
server { 
        listen 80; 
        server_name *.test.com; 
        if ( $http_host ~* "^(.*)\.test\.com$") { 
                set $domain $1; 
                rewrite ^(.*) http://www.test.com/test/$domain/ break; 
        } 
}
方法二、
 
当访问http://abc.test.com跳转到http://www.test.com/test/abc/
server {
        listen 80; 
        server_name *.test.com; 
        root /usr/local/www; 
        #这是里可以加多个目录,如果不加目录,会无法访问到abc.test.com/目录下的文件,如图片目录/images
        location ~ ^/(test|images|styles)/ 
        { 
                proxy_redirect        off; 
                proxy_set_header    Host   www.test.com; 
                proxy_pass      http://192.168.1.2:8080; 
        } 
        location / { 
                set $domain default; 
                if ( $http_host ~* "^(.*)\.test\.com$") { 
                        set $domain $1; 
                } 
                rewrite ^/(.*)    /test/$domain/$1 last; 
        } 
        access_log off;
}
 
rewrite命令
nginx的rewrite相当于apache的rewriterule(大多数情况下可以把原有apache的rewrite规则加上引号就可以直接使用),它可以用在server,location 和IF条件判断块中,命
 
令格式如下:
rewrite 正则表达式 替换目标 flag标记
flag标记可以用以下几种格式:
last - 基本上都用这个Flag。
break - 中止Rewirte,不在继续匹配
redirect - 返回临时重定向的HTTP状态302
permanent - 返回永久重定向的HTTP状态301
例如下面这段设定nginx将某个目录下面的文件重定向到另一个目录,$2对应第二个括号(.*)中对应的字符串:
 
location /download/ {
        rewrite ^(/download/.*)/m/(.*)\..*$ $1/nginx-rewrite/$2.gz break;
}
 
nginx重定向的IF条件判断
在server和location两种情况下可以使用nginx的IF条件判断,条件可以为以下几种:
正则表达式
 
如:
匹配判断
~ 为区分大小写匹配; !~为区分大小写不匹配
~* 为不区分大小写匹配;!~为不区分大小写不匹配
 
就是当用户输入 www.a.com.cn 自动跳转到www.a.com 这个域名:
 
rewrite ^/(.*)$ http://www.a.com/$1 permanent; 或者cname
例如下面设定nginx在用户使用ie的使用重定向到/nginx-ie目录下:
if ($http_user_agent ~ MSIE) {
        rewrite ^(.*)$ /nginx-ie/$1 break;
}
文件和目录判断
-f和!-f判断是否存在文件
-d和!-d判断是否存在目录
-e和!-e判断是否存在文件或目录
-x和!-x判断文件是否可执行
 
例如下面设定nginx在文件和目录不存在的时候重定向:
if (!-e $request_filename) {
        proxy_pass http://127.0.0.1;
}
 
return
返回http代码,例如设置nginx防盗链:
location ~* \.(gif|jpg|png|swf|flv)$ {
        valid_referers none blocked www.jefflei.comwww.leizhenfang.com;
        if ($invalid_referer) {
                return 404;
        }
}
 
记一正则,匹配非某单词
由于要rewrite一个地址从
/mag/xx/xxx/ -> /m/xxx
但原先 /mag/xx/more/ 要保留
这就得写一个比较奇特的正则了,尝试了比较多的写法也没成功
 
最先想的是:
 
location ~* ^/mag/[^/]+/[^(more)]+/ {
  rewrite ^/mag/[^/]+/(.*) /m/$1 permanent;
}
 
 
[]的写法并不凑效,里面是匹配单个字符的,这样无效,匹配不了
 
还是小范同学不错,研究的深入,提供了非某单词的写法 (?!more)
 
location ~* ^/mag/[^/]+/(?!more)([^/]+)/ {
  rewrite ^/mag/[^/]+/(.*) /m/$1 permanent;
}
 
 
这个写法勉强可以应付了,后面的匹配单元虽说还不完美,但也能够对付我的所有需求内容了。
有需要的可以参考此写法。
 
引用
 
常用分组语法
 
捕获
(exp) 匹配exp,并捕获文本到自动命名的组里 
(?exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp) 
(?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号 
 
零宽断言 
(?=exp) 匹配exp前面的位置 
(?<=exp) 匹配exp后面的位置 
(?!exp) 匹配后面跟的不是exp的位置 
(?
为何要使用301重定向
在网站建设中需要网页重定向的情况很多:如网页目录结构变动,网页重命名、网页的扩展名改变、网站域名改变等。如果不做重 定向,用户的收藏和搜索引擎数据库中的旧地址只能让访客得到一个404错误信息页面,访问流量白白丧失。不仅如此,之前该页面的一切积累(比如PR值)就 都白费了。
301重定向不仅能使页面实现自动跳转,对于搜索引擎来说,也可能可以传递PR值。
nginx重定向规则详细介绍
http://www.jefflei.com/post/1015.html
rewrite命令
nginx的rewrite相当于apache的rewriterule(大多数情况下可以把原有apache的rewrite规则加上引号就可以直接使用),它可以用在server,location 和IF条件判断块中,命令格式如下:
rewrite 正则表达式 替换目标 flag标记
flag标记可以用以下几种格式:
last – 基本上都用这个Flag。
break – 中止Rewirte,不在继续匹配
redirect – 返回临时重定向的HTTP状态302
permanent – 返回永久重定向的HTTP状态301
例如下面这段设定nginx将某个目录下面的文件重定向到另一个目录,$2对应第二个括号(.*)中对应的字符串:
location /download/ {
rewrite ^(/download/.*)/m/(.*)\..*$ $1/nginx-rewrite/$2.gz break;
}
nginx重定向的IF条件判断
在server和location两种情况下可以使用nginx的IF条件判断,条件可以为以下几种:
正则表达式
如:
匹配判断
~  为区分大小写匹配; !~为区分大小写不匹配
 ~* 为不区分大小写匹配;!~为不区分大小写不匹配
例如下面设定nginx在用户使用ie的使用重定向到/nginx-ie目录下:
if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /nginx-ie/$1 break;
}
文件和目录判断
  -f和!-f判断是否存在文件
 -d和!-d判断是否存在目录
 -e和!-e判断是否存在文件或目录
 -x和!-x判断文件是否可执行
例如下面设定nginx在文件和目录不存在的时候重定向:
if (!-e $request_filename) {
proxy_pass http://127.0.0.1/;
}
return
返回http代码,例如设置nginx防盗链:
location ~* \.(gif|jpg|png|swf|flv)$ {
valid_referers none blocked http://www.jefflei.com/ http://www.leizhenfang.com/;
if ($invalid_referer) {
return 404;
}
}
set
设置nginx变量
 
301重定向方法
进行了301重定向,把www .jefflei.com和jefflei.com合并,并把之前的域名也一并合并. 有两种实现方法,第一种方法是判断nginx核心变量host(老版本是http_host):
server {
server_name www.jefflei.com jefflei.com ;
if ($host != 'www.jefflei.com' ) {
rewrite ^/(.*)$ http://www.jefflei.com/$1 permanent;
}
...
}
第二种方法:
server {
server_name jefflei.com;
rewrite ^/(.*) http://www.jefflei.com/$1 permanent;
}
测试了第一种方法ok,这两种方法中, permanent是关键,详细说明见nginx重定向规则说明。
last – 基本上都用这个Flag。
break – 中止Rewirte,不在继续匹配
redirect – 返回临时重定向的HTTP状态302
permanent – 返回永久重定向的HTTP状态301
好了,现在可以检查结果,这里可以看返回的HTTP头信息:
http://www.seoconsultants.com/tools/headers.asp
第二种方法没有测试成功...
 
测试是否定向成功
http://qinfy.net/301-redirect-for-nginx/
输入指令~
/usr/local/nginx/sbin/nginx -t
提示:
the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
configuration file /usr/local/nginx/conf/nginx.conf test is successful
测试成功~ 重启nginx~ 输入指令~
/usr/local/nginx/sbin/nginx -s reload
重启之后测试一下~是否成功设定完成! 输入指令~
curl -I imcat.tk
 
会输出:
HTTP/1.1 301 Moved Permanently
Server: nginx/0.7.65
Date: Tue, 03 Aug 2010 01:12:37 GMT
Content-Type: text/html
Content-Length: 185
Connection: keep-alive
Location: http://qinfy.net/
nginx rewrite 伪静态配置参数详细说明(转)
http://hi.baidu.com/hx10/blog/item/942a0ad784f3ffd0a144df94.html
nginx rewrite 伪静态配置参数和使用例子 附正则使用说明
正则表达式匹配,其中:
* ~ 为区分大小写匹配 
* ~* 为不区分大小写匹配 
* !~和!~*分别为区分大小写不匹配及不区分大小写不匹配
 
文件及目录匹配,其中:
* -f和!-f用来判断是否存在文件 
* -d和!-d用来判断是否存在目录 
* -e和!-e用来判断是否存在文件或目录 
* -x和!-x用来判断文件是否可执行
flag标记有:
* last 相当于Apache里的[L]标记,表示完成rewrite 
* break 终止匹配, 不再匹配后面的规则 
* redirect 返回302临时重定向 地址栏会显示跳转后的地址 
* permanent 返回301永久重定向 地址栏会显示跳转后的地址
一些可用的全局变量有,可以用做条件判断(待补全)
$args 
$content_length 
$content_type 
$document_root 
$document_uri 
$host 
$http_user_agent 
$http_cookie 
$limit_rate 
$request_body_file 
$request_method 
$remote_addr 
$remote_port 
$remote_user 
$request_filename 
$request_uri 
$query_string 
$scheme 
$server_protocol 
$server_addr 
$server_name 
$server_port 
$uri
结合QeePHP的例子
if (!-d $request_filename) { 
rewrite ^/([a-z-A-Z]+)/([a-z-A-Z]+)/?(.*)$ /index.php?namespace=user&controller=$1&action=$2&$3 last; 
rewrite ^/([a-z-A-Z]+)/?$ /index.php?namespace=user&controller=$1 last; 
break;
多目录转成参数
abc.domian.com/sort/2 => abc.domian.com/index.php?act=sort&name=abc&id=2
if ($host ~* (.*)\.domain\.com) { 
set $sub_name $1; 
rewrite ^/sort\/(\d+)\/?$ /index.php?act=sort&cid=$sub_name&id=$1 last; 
}
目录对换
/123456/xxxx -> /xxxx?id=123456
rewrite ^/(\d+)/(.+)/ /$2?id=$1 last;
例如下面设定nginx在用户使用ie的使用重定向到/nginx-ie目录下:
if ($http_user_agent ~ MSIE) { 
rewrite ^(.*)$ /nginx-ie/$1 break; 
}
目录自动加“/”
if (-d $request_filename){ 
rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent; 
}
禁止htaccess
location ~/\.ht { 
deny all; 
}
禁止多个目录
location ~ ^/(cron|templates)/ { 
deny all; 
break; 
}
禁止以/data开头的文件
可以禁止/data/下多级目录下.log.txt等请求;
location ~ ^/data { 
deny all; 
}
禁止单个目录
不能禁止.log.txt能请求
location /searchword/cron/ { 
deny all; 
}
禁止单个文件
location ~ /data/sql/data.sql { 
deny all; 
}
给favicon.ico和robots.txt设置过期时间;
这里为favicon.ico为99 天,robots.txt为7天并不记录404错误日志
location ~(favicon.ico) { 
log_not_found off; 
expires 99d; 
break; 
 
location ~(robots.txt) { 
log_not_found off; 
expires 7d; 
break; 
}
设定某个文件的过期时间;这里为600秒,并不记录访问日志
location ^~ /html/scripts/loadhead_1.js { 
access_log off; 
root /opt/lampp/htdocs/web; 
expires 600; 
break; 
}
文件反盗链并设置过期时间
这里的return 412 为自定义的http状态码,默认为403,方便找出正确的盗链的请求
“rewrite ^/ http://leech.c1gstudio.com/leech.gif;”显示一张防盗链图片
“access_log off;”不记录访问日志,减轻压力
“expires 3d”所有文件3天的浏览器缓存
location ~* ^.+\.(jpg|jpeg|gif|png|swf|rar|zip|css|js)$ { 
valid_referers none blocked *.c1gstudio.com *.c1gstudio.net localhost 208.97.167.194; 
if ($invalid_referer) { 
rewrite ^/ http://leech.c1gstudio.com/leech.gif; 
return 412; 
break; 
access_log off; 
root /opt/lampp/htdocs/web; 
expires 3d; 
break; 
}
只充许固定ip访问网站,并加上密码
root /opt/htdocs/www; 
allow 208.97.167.194; 
allow 222.33.1.2; 
allow 231.152.49.4; 
deny all; 
auth_basic "C1G_ADMIN"; 
auth_basic_user_file htpasswd;
将多级目录下的文件转成一个文件,增强seo效果
/job-123-456-789.html 指向/job/123/456/789.html
rewrite ^/job-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /job/$1/$2/jobshow_$3.html last;
将根目录下某个文件夹指向2级目录
如/shanghaijob/ 指向 /area/shanghai/
如果你将last改成permanent,那么浏览器地址栏显是 /location/shanghai/
rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;
上面例子有个问题是访问/shanghai 时将不会匹配
rewrite ^/([0-9a-z]+)job$ /area/$1/ last; 
rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;
这样/shanghai 也可以访问了,但页面中的相对链接无法使用,
如./list_1.html真实地址是/area /shanghia/list_1.html会变成/list_1.html,导至无法访问。
那我加上自动跳转也是不行咯
(-d $request_filename)它有个条件是必需为真实目录,而我的rewrite不是的,所以没有效果
if (-d $request_filename){ 
rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent; 
}
知道原因后就好办了,让我手动跳转吧
rewrite ^/([0-9a-z]+)job$ /$1job/ permanent; 
rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;
文件和目录不存在的时候重定向:
if (!-e $request_filename) { 
proxy_pass http://127.0.0.1/; 
}
域名跳转
server 
listen 80; 
server_name jump.c1gstudio.com; 
index index.html index.htm index.php; 
root /opt/lampp/htdocs/www; 
rewrite ^/ http://www.c1gstudio.com/; 
access_log off; 
}
多域名转向
server_name http://www.c1gstudio.com/ http://www.c1gstudio.net/; 
index index.html index.htm index.php; 
root /opt/lampp/htdocs; 
if ($host ~ "c1gstudio\.net") { 
rewrite ^(.*) http://www.c1gstudio.com$1/ permanent; 
}
三级域名跳转
if ($http_host ~* "^(.*)\.i\.c1gstudio\.com$") { 
rewrite ^(.*) http://top.yingjiesheng.com$1/; 
break; 
}
域名镜向
server 
listen 80; 
server_name mirror.c1gstudio.com; 
index index.html index.htm index.php; 
root /opt/lampp/htdocs/www; 
rewrite ^/(.*) http://www.c1gstudio.com/$1 last; 
access_log off; 
}
某个子目录作镜向
location ^~ /zhaopinhui { 
rewrite ^.+ http://zph.c1gstudio.com/ last; 
break; 
}
discuz ucenter home (uchome) rewrite
rewrite ^/(space|network)-(.+)\.html$ /$1.php?rewrite=$2 last; 
rewrite ^/(space|network)\.html$ /$1.php last; 
rewrite ^/([0-9]+)$ /space.php?uid=$1 last;
discuz 7 rewrite
rewrite ^(.*)/archiver/((fid|tid)-[\w\-]+\.html)$ $1/archiver/index.php?$2 last; 
rewrite ^(.*)/forum-([0-9]+)-([0-9]+)\.html$ $1/forumdisplay.php?fid=$2&page=$3 last; 
rewrite ^(.*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/viewthread.php?tid=$2&extra=page\%3D$4&page=$3 last; 
rewrite ^(.*)/profile-(username|uid)-(.+)\.html$ $1/viewpro.php?$2=$3 last; 
rewrite ^(.*)/space-(username|uid)-(.+)\.html$ $1/space.php?$2=$3 last; 
rewrite ^(.*)/tag-(.+)\.html$ $1/tag.php?name=$2 last;
给discuz某版块单独配置域名
server_name bbs.c1gstudio.com news.c1gstudio.com; 
 
location = / { 
if ($http_host ~ news\.c1gstudio.com$) { 
rewrite ^.+ http://news.c1gstudio.com/forum-831-1.html last; 
break; 
}
discuz ucenter 头像 rewrite 优化
location ^~ /ucenter { 
location ~ .*\.php?$ 
#fastcgi_pass unix:/tmp/php-cgi.sock; 
fastcgi_pass 127.0.0.1:9000; 
fastcgi_index index.php; 
include fcgi.conf; 
 
location /ucenter/data/avatar { 
log_not_found off; 
access_log off; 
location ~ /(.*)_big\.jpg$ { 
error_page 404 /ucenter/images/noavatar_big.gif; 
location ~ /(.*)_middle\.jpg$ { 
error_page 404 /ucenter/images/noavatar_middle.gif; 
location ~ /(.*)_small\.jpg$ { 
error_page 404 /ucenter/images/noavatar_small.gif; 
expires 300; 
break; 
}
jspace rewrite
location ~ .*\.php?$ 
#fastcgi_pass unix:/tmp/php-cgi.sock; 
fastcgi_pass 127.0.0.1:9000; 
fastcgi_index index.php; 
include fcgi.conf; 
 
location ~* ^/index.php/ 
rewrite ^/index.php/(.*) /index.php?$1 break; 
fastcgi_pass 127.0.0.1:9000; 
fastcgi_index index.php; 
include fcgi.conf; 
}
Tags: ,

有时我们想截取最后一个斜杠'/'后面的数字;

有时我们又需要截取第一个斜杠'/'前面的内容判断用户输入的url链接带不带http://等等;

字符串的截取php内置函数就有很多种;简单的写几个示例;

1
$str='123/456/789/abc';

截取第一个斜杠前面的内容可以这样来:

1
echo substr($str,0,strpos($str'/'))

    或者

1
2
3
$array=explode('/'$str);
echo $array[0];
// 输出 123

截取第一个斜杠后面的内容可以这样来:

1
2
echo substr($str,strpos($str,'/')+1);
//输出 456/789/abc

 

截取最后一个斜杠后面的内容可以这样来:

1
echo trim(strrchr($str'/'),'/');

    或者如果知道斜杠的个数

1
2
3
$array=explode('/'$str);
echo $array[3];
//输出 abc

 

但是问题来了;如果不知道有多少个斜杠呢?如果想要第二个斜杠和第三个斜杠中间的内容呢?

下面我写的这个函数就可以轻松解决如上 所有问题;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * 按符号截取字符串的指定部分
 * @param string $str 需要截取的字符串
 * @param string $sign 需要截取的符号
 * @param int $number 如是正数以0为起点从左向右截  负数则从右向左截
 * @return string 返回截取的内容
 */
 
function cut_str($str,$sign,$number){
    $array=explode($sign$str);
    $length=count($array);
    if($number<0){
        $new_array=array_reverse($array);
        $abs_number=abs($number);
        if($abs_number>$length){
            return 'error';
        }else{
            return $new_array[$abs_number-1];
        }
    }else{
        if($number>=$length){
            return 'error';
        }else{
            return $array[$number];
        }
    }
}

示例:

1
2
3
4
echo cut_str($str,'/',0); //输出 123
echo cut_str($str,'/',2); //输出 789
echo cut_str($str,'/',-1);//输出 abc
echo cut_str($str,'/',-3);//输出 456
Tags: ,

js判断滚动条滚动方向

[不指定 2016/10/25 10:09 | by 刘新修 ]
JavaScript代码
  1. //第一种写法  
  2.         var a;  
  3.             
  4.         function scroll( fn ) {  
  5.             var beforeScrollTop = document.body.scrollTop,  
  6.                 fn = fn || function() {};  
  7.             window.addEventListener("scroll", function() {  
  8.                 var afterScrollTop = document.body.scrollTop,  
  9.                     delta = afterScrollTop - beforeScrollTop;  
  10.                 if( delta === 0 ) return false;  
  11.                 fn( delta > 0 ? "down" : "up" );  
  12.                 beforeScrollTop = afterScrollTop;  
  13.             }, false);  
  14.         }  
  15.         scroll(function(direction) {  
  16.             // console.log(direction);  
  17.             a =  direction;  
  18.         });  
  19.         // 第二种写法  
  20.         var a;  
  21.             
  22.         function scroll( fn ) {  
  23.             var beforeScrollTop = document.body.scrollTop,  
  24.                 fn = fn || function() {};  
  25.             window.addEventListener("scroll", function() {  
  26.                 var afterScrollTop = document.body.scrollTop,  
  27.                     delta = afterScrollTop - beforeScrollTop;  
  28.                 if( delta === 0 ) return false;  
  29.                 a = fn( delta > 0 ? "down" : "up" );  
  30.                 beforeScrollTop = afterScrollTop;  
  31.             }, false);  
  32.         }  
  33.         scroll(function(direction) {  
  34.             // console.log(direction);  
  35.             return direction;  
  36.         });  
  37.         // 第三种写法-----=是执行完事件后的回调函数  
  38.   
  39.         var a;  
  40.         var beforeScrollTop = document.body.scrollTop,  
  41.             fn = fn || function() {};  
  42.         window.addEventListener("scroll", function() {  
  43.             var afterScrollTop = document.body.scrollTop,  
  44.                 delta = afterScrollTop - beforeScrollTop;  
  45.             if( delta === 0 ) return false;  
  46.             fn( delta > 0 ? "down" : "up" );  
  47.             beforeScrollTop = afterScrollTop;  
  48.         }, false);  
  49.         function fn(direction) {  
  50.             // console.log(direction);  
  51.             a = direction;  
  52.         };  
  53.   
  54.         window.onscroll=function(){  
  55.             console.log(a);  
  56.         }  
Tags: ,

component的生命周期图

getDefaultProps

object getDefaultProps()

执行过一次后,被创建的类会有缓存,映射的值会存在this.props,前提是这个prop不是父组件指定的 
这个方法在对象被创建之前执行,因此不能在方法内调用this.props ,另外,注意任何getDefaultProps()返回的对象在实例中共享,不是复制

getInitialState

object getInitialState()

控件加载之前执行,返回值会被用于state的初始化值

componentWillMount

void componentWillMount()

执行一次,在初始化render之前执行,如果在这个方法内调用setStaterender()知道state发生变化,并且只执行一次

render

ReactElement render()

render的时候会调用render()会被调用 
调用render()方法时,首先检查this.propsthis.state返回一个子元素,子元素可以是DOM组件或者其他自定义复合控件的虚拟实现 
如果不想渲染可以返回null或者false,这种场景下,React渲染一个<noscript>标签,当返回null或者false时,ReactDOM.findDOMNode(this)返回null 
render()方法是很纯净的,这就意味着不要在这个方法里初始化组件的state,每次执行时返回相同的值,不会读写DOM或者与服务器交互,如果必须如服务器交互,在componentDidMount()方法中实现或者其他生命周期的方法中实现,保持render()方法纯净使得服务器更准确,组件更简单

componentDidMount

void componentDidMount()

在初始化render之后只执行一次,在这个方法内,可以访问任何组件,componentDidMount()方法中的子组件在父组件之前执行

从这个函数开始,就可以和 JS 其他框架交互了,例如设置计时 setTimeout 或者 setInterval,或者发起网络请求

shouldComponentUpdate

boolean shouldComponentUpdate(   object nextProps, object nextState )
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

这个方法在初始化render时不会执行,当props或者state发生变化时执行,并且是在render之前,当新的props或者state不需要更新组件时,返回false

shouldComponentUpdate: function(nextProps, nextState) {   return nextProps.id !== this.props.id; }
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

shouldComponentUpdate方法返回false时,讲不会执行render()方法,componentWillUpdatecomponentDidUpdate方法也不会被调用

默认情况下,shouldComponentUpdate方法返回true防止state快速变化时的问题,但是如果·state不变,props只读,可以直接覆盖shouldComponentUpdate用于比较propsstate的变化,决定UI是否更新,当组件比较多时,使用这个方法能有效提高应用性能

componentWillUpdate

void componentWillUpdate(   object nextProps, object nextState )
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

propsstate发生变化时执行,并且在render方法之前执行,当然初始化render时不执行该方法,需要特别注意的是,在这个函数里面,你就不能使用this.setState来修改状态。这个函数调用之后,就会把nextPropsnextState分别设置到this.propsthis.state中。紧接着这个函数,就会调用render()来更新界面了

componentDidUpdate

void componentDidUpdate(   object prevProps, object prevState )
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

组件更新结束之后执行,在初始化render时不执行

componentWillReceiveProps

void componentWillReceiveProps(   object nextProps )
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

props发生变化时执行,初始化render时不执行,在这个回调函数里面,你可以根据属性的变化,通过调用this.setState()来更新你的组件状态,旧的属性还是可以通过this.props来获取,这里调用更新状态是安全的,并不会触发额外的render调用

componentWillReceiveProps: function(nextProps) {   this.setState({     likesIncreasing: nextProps.likeCount > this.props.likeCount   }); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

componentWillUnmount

void componentWillUnmount()

当组件要被从界面上移除的时候,就会调用componentWillUnmount(),在这个函数中,可以做一些组件相关的清理工作,例如取消计时器、网络请求等

总结

React Native的生命周期就介绍完了,其中最上面的虚线框和右下角的虚线框的方法一定会执行,左下角的方法根据props state是否变化去执行,其中建议只有在componentWillMount,componentDidMount,componentWillReceiveProps方法中可以修改state

英文地址:https://facebook.github.io/react/docs/component-specs.html#lifecycle-methods

组件的生命周期

组件的生命周期分成三个状态:

  • Mounting:已插入真实 DOM

  • Updating:正在被重新渲染

  • Unmounting:已移出真实 DOM

React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。

  • componentWillMount()

  • componentDidMount()

  • componentWillUpdate(object nextProps, object nextState)

  • componentDidUpdate(object prevProps, object prevState)

  • componentWillUnmount()

此外,React 还提供两种特殊状态的处理函数。

  • componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用

  • shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用

Tags: ,

本站测试实例访问地址: http://code.liuxinxiu.com/php/Interface/html/WebSocket.html

XML/HTML代码
  1. <!DOCTYPE html>  
  2. <html>  
  3.     <head>  
  4.         <title>chatdemo</title>  
  5.         <meta charset="utf-8">  
  6.         <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, user-scalable=no">  
  7.         <link href="http://code.liuxinxiu.com/lib/bootstrap/3.3.2/bootstrap.min.css" rel="stylesheet">  
  8.         <style type="text/css">  
  9.         <!--  
  10.         html, body {  
  11.           min-height: 100%; }  
  12.   
  13.         body {  
  14.           margin: 0;  
  15.           padding: 0;  
  16.           width: 100%;  
  17.           font-family: "Microsoft Yahei",sans-serif, Arial; }  
  18.   
  19.         .container {  
  20.           text-align: center; }  
  21.   
  22.         .title {  
  23.           font-size: 16px;  
  24.           color: rgba(0, 0, 0, 0.3);  
  25.           position: fixed;  
  26.           z-index:1000;  
  27.           line-height: 30px;  
  28.           height: 30px;  
  29.           left: 0px;  
  30.           right: 0px;  
  31.           background-color: white; }  
  32.   
  33.         .content {  
  34.           background-color: #f1f1f1;  
  35.           border-top-left-radius: 6px;  
  36.           border-top-right-radius: 6px;  
  37.           margin-top: 30px; }  
  38.           .content .show-area {  
  39.             text-align: left;  
  40.             padding-top: 8px;  
  41.             padding-bottom: 168px; }  
  42.             .content .show-area .message {  
  43.               width: 70%;  
  44.               padding: 5px;  
  45.               word-wrap: break-word;  
  46.               word-break: normal; }  
  47.           .content .write-area {  
  48.             position: fixed;  
  49.             bottom: 0px;  
  50.             right: 0px;  
  51.             left: 0px;  
  52.             background-color: #f1f1f1;  
  53.             z-index: 10;  
  54.             width: 100%;  
  55.             height: 160px;  
  56.             border-top: 1px solid #d8d8d8; }  
  57.             .content .write-area .send {  
  58.               position: relative;  
  59.               top: -28px;  
  60.               height: 28px;  
  61.               border-top-left-radius: 55px;  
  62.               border-top-right-radius: 55px; }  
  63.             .content .write-area #name{  
  64.               position: relative;  
  65.               top: -20px;  
  66.               line-height: 28px;  
  67.               font-size: 13px; }  
  68.         -->  
  69.         </style>  
  70.     </head>  
  71.     <body>  
  72.         <div class="container">  
  73.             <div class="title">简易聊天demo</div>  
  74.             <div class="content">  
  75.                 <div class="show-area"></div>  
  76.                 <div class="write-area">  
  77.                     <div><button class="btn btn-default send" >发送</button></div>  
  78.                     <div><input name="name" id="name" type="text" placeholder="input your name"></div>  
  79.                     <div>  
  80.                         <textarea name="message" id="message" cols="38" rows="4" placeholder="input your message..."></textarea>  
  81.                     </div>                      
  82.                 </div>  
  83.             </div>  
  84.         </div>  
  85.   
  86.         <script src="http://code.liuxinxiu.com/lib/jquery/1.9.1/jquery.min.js"></script>  
  87.         <script src="http://code.liuxinxiu.com/lib/bootstrap/3.3.2/bootstrap.min.js"></script>  
  88.         <script>  
  89.         $(function(){  
  90.         var wsurl='ws://code.liuxinxiu.com:9090/php/webSocket/server.php';  
  91.             var websocket;  
  92.             var i = 0;  
  93.             /******** 判断是否有webSocket对象 *******/  
  94.             if(window.WebSocket){  
  95.                 websocket=new WebSocket(wsurl);  
  96.   
  97.                 /******** 连接建立||发起webSocket连接 ********/  
  98.                 websocket.onopen = function(evevt){  
  99.                     console.log("Connected to WebSocket server.");  
  100.                     /******** 监听ready状态码 ********/  
  101.                     console.log('websocket.readyState:'+websocket.readyState);  
  102.                     /*********************************************************  
  103.                       值为0值表示该连接尚未建立  
  104.                       值为1表示连接建立和沟通是可能的  
  105.                       值为2表示连接是通过将结束握手  
  106.                       值为3表示连接已关闭或无法打开  
  107.                     *********************************************************/  
  108.                     /******** 判断状态码为1则连接成功即可正常通信********/  
  109.                     if(websocket.readyState==1){  
  110.                         $('.show-area').append('<p class="bg-info message"><i class="glyphicon glyphicon-info-sign"></i>Connected to WebSocket server!</p>');  
  111.                     }  
  112.                 }  
  113.                 //收到消息  
  114.                 websocket.onmessage = function(event) {  
  115.                     var msg = JSON.parse(event.data); //解析收到的json消息数据  
  116.                     console.log("\n--->>message:\n"+event.data);  
  117.   
  118.                     var type = msg.type; // 消息类型  
  119.                     var umsg = msg.message; //消息文本  
  120.                     var uname = msg.name; //发送人  
  121.                     i++;  
  122.                     if(type == 'usermsg'){  
  123.                         $('.show-area').append('<p class="bg-success message"><i class="glyphicon glyphicon-user"></i><a name="'+i+'"></a><span class="label label-primary">'+uname+' say: </span>'+umsg+'</p>');  
  124.                     }  
  125.                     if(type == 'system'){  
  126.                         $('.show-area').append('<p class="bg-warning message"><a name="'+i+'"></a><i class="glyphicon glyphicon-info-sign"></i>'+umsg+'</p>');  
  127.                     }  
  128.                       
  129.                     $('#message').val('');  
  130.                     window.location.hash = '#'+i;  
  131.                 }  
  132.   
  133.                 //发生错误  
  134.                 websocket.onerror = function(event){  
  135.                     i++;  
  136.                     console.log("Connected to WebSocket server error");  
  137.                     $('.show-area').append('<p class="bg-danger message"><a name="'+i+'"></a><i class="glyphicon glyphicon-info-sign"></i>Connect to WebSocket server error.</p>');  
  138.                     window.location.hash = '#'+i;  
  139.                 }  
  140.   
  141.                 //连接关闭  
  142.                 websocket.onclose = function(event){  
  143.                     i++;  
  144.                     console.log('websocket Connection Closed. ');  
  145.                     $('.show-area').append('<p class="bg-warning message"><a name="'+i+'"></a><i class="glyphicon glyphicon-info-sign"></i>websocket Connection Closed.</p>');  
  146.                     window.location.hash = '#'+i;  
  147.                 }  
  148.   
  149.                 function send(){  
  150.                     var name = $('#name').val();  
  151.                     var message = $('#message').val();  
  152.                     if(!name){  
  153.                         alert('请输入用户名!');  
  154.                         return false;  
  155.                     }  
  156.                     if(!message){  
  157.                         alert('发送消息不能为空!');  
  158.                         return false;  
  159.                     }  
  160.                     var msg = {  
  161.                         message: message,  
  162.                         name: name  
  163.                     };  
  164.                     try{    
  165.                         websocket.send(JSON.stringify(msg));  
  166.                     } catch(ex) {    
  167.                         console.log(ex);  
  168.                     }    
  169.                 }  
  170.   
  171.                 //按下enter键发送消息  
  172.                 $(window).keydown(function(event){  
  173.                     if(event.keyCode == 13){  
  174.                         console.log('user enter');  
  175.                         send();  
  176.                     }  
  177.                 });  
  178.   
  179.                 //点发送按钮发送消息  
  180.                 $('.send').bind('click',function(){  
  181.                     send();  
  182.                 });  
  183.                   
  184.             }  
  185.             else{  
  186.                 alert('该浏览器不支持web socket');  
  187.             }  
  188.   
  189.         });      
  190.         </script>          
  191.     </body>  
  192. </html>  

后端PHP代码部分:

PHP代码
  1. 2.php code:  
  2. <?php  
  3. $host = '127.0.0.1';  
  4. $port = '9090';  
  5. $null = NULL;  
  6.   
  7. //创建tcp socket  
  8. $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);  
  9. socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);  
  10. socket_bind($socket, 0, $port);  
  11.   
  12. //监听端口  
  13. socket_listen($socket);  
  14.   
  15. //连接的client socket 列表  
  16. $clients = array($socket);  
  17.   
  18. //设置一个死循环,用来监听连接 ,状态  
  19. while (true) {  
  20.       
  21.     $changed = $clients;  
  22.     socket_select($changed, $null, $null, 0, 10);  
  23.       
  24.     //如果有新的连接  
  25.     if (in_array($socket, $changed)) {  
  26.         //接受并加入新的socket连接  
  27.         $socket_new = socket_accept($socket);  
  28.         $clients[] = $socket_new;  
  29.           
  30.         //通过socket获取数据执行handshake  
  31.         $header = socket_read($socket_new, 1024);  
  32.         perform_handshaking($header, $socket_new, $host, $port);  
  33.           
  34.         //获取client ip 编码json数据,并发送通知  
  35.         socket_getpeername($socket_new, $ip);  
  36.         $response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' connected')));  
  37.         send_message($response);  
  38.         $found_socket = array_search($socket, $changed);  
  39.         unset($changed[$found_socket]);  
  40.     }  
  41.       
  42.     //轮询 每个client socket 连接  
  43.     foreach ($changed as $changed_socket) {      
  44.           
  45.         //如果有client数据发送过来  
  46.         while(socket_recv($changed_socket, $buf, 1024, 0) >= 1)  
  47.         {  
  48.             //解码发送过来的数据  
  49.             $received_text = unmask($buf);  
  50.             $tst_msg = json_decode($received_text);    
  51.             $user_name = $tst_msg->name;  
  52.             $user_message = $tst_msg->message;  
  53.               
  54.             //把消息发送回所有连接的 client 上去  
  55.             $response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>$user_name, 'message'=>$user_message)));  
  56.             send_message($response_text);  
  57.             break 2;  
  58.         }  
  59.           
  60.         //检查offline的client  
  61.         $buf = @socket_read($changed_socket, 1024, PHP_NORMAL_READ);  
  62.         if ($buf === false) {  
  63.             $found_socket = array_search($changed_socket, $clients);  
  64.             socket_getpeername($changed_socket, $ip);  
  65.             unset($clients[$found_socket]);  
  66.             $response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' disconnected')));  
  67.             send_message($response);  
  68.         }  
  69.     }  
  70. }  
  71. // 关闭监听的socket  
  72. socket_close($sock);  
  73.   
  74. //发送消息的方法  
  75. function send_message($msg)  
  76. {  
  77.     global $clients;  
  78.     foreach($clients as $changed_socket)  
  79.     {  
  80.         @socket_write($changed_socket,$msg,strlen($msg));  
  81.     }  
  82.     return true;  
  83. }  
  84.   
  85.   
  86. //解码数据  
  87. function unmask($text) {  
  88.     $length = ord($text[1]) & 127;  
  89.     if($length == 126) {  
  90.         $masks = substr($text, 4, 4);  
  91.         $data = substr($text, 8);  
  92.     }  
  93.     elseif($length == 127) {  
  94.         $masks = substr($text, 10, 4);  
  95.         $data = substr($text, 14);  
  96.     }  
  97.     else {  
  98.         $masks = substr($text, 2, 4);  
  99.         $data = substr($text, 6);  
  100.     }  
  101.     $text = "";  
  102.     for ($i = 0; $i < strlen($data); ++$i) {  
  103.         $text .= $data[$i] ^ $masks[$i%4];  
  104.     }  
  105.     return $text;  
  106. }  
  107.   
  108. //编码数据  
  109. function mask($text)  
  110. {  
  111.     $b1 = 0x80 | (0x1 & 0x0f);  
  112.     $length = strlen($text);  
  113.       
  114.     if($length <= 125)  
  115.         $header = pack('CC', $b1, $length);  
  116.     elseif($length > 125 && $length < 65536)  
  117.         $header = pack('CCn', $b1, 126, $length);  
  118.     elseif($length >= 65536)  
  119.         $header = pack('CCNN', $b1, 127, $length);  
  120.     return $header.$text;  
  121. }  
  122.   
  123. //握手的逻辑  
  124. function perform_handshaking($receved_header,$client_conn, $host, $port)  
  125. {  
  126.     $headers = array();  
  127.     $lines = preg_split("/\r\n/", $receved_header);  
  128.     foreach($lines as $line)  
  129.     {  
  130.         $line = chop($line);  
  131.         if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))  
  132.         {  
  133.             $headers[$matches[1]] = $matches[2];  
  134.         }  
  135.     }  
  136.   
  137.     $secKey = $headers['Sec-WebSocket-Key'];  
  138.     $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));  
  139.     $upgrade  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .  
  140.     "Upgrade: websocket\r\n" .  
  141.     "Connection: Upgrade\r\n" .  
  142.     "WebSocket-Origin: $host\r\n" .  
  143.     "WebSocket-Location: ws://$host:$port/demo/shout.php\r\n".  
  144.     "Sec-WebSocket-Accept:$secAccept\r\n\r\n";  
  145.     socket_write($client_conn,$upgrade,strlen($upgrade));  
  146. }  
Tags: ,

关于Javascript的this

[不指定 2016/09/27 16:54 | by 刘新修 ]
this是Javascript语言的一个关键字,它是一个指针,根据不同情形下指向不同的对象,随着函数使用场合的不同,this的值会发生变化。
 
记住一条:当function被作为method调用时,this指向调用对象。另外,JavaScript并不是OO的,而是object based的一种语言。之所以你会觉得this用起来混乱,是因为你还没理解JavaScript的诸如全局对象、event handler等一些机制。
 
this指向变化通常有以下几种情形:
 
情况一:纯粹的函数调用[作为method调用]
 
JavaScript代码
  1. function test(){  
  2.   this.x = 1;  
  3.   alert(this.x);  
  4. }  
  5. test(); // 1  
以上function属于全局性调用,因此this就代表全局对象Global,为了证明它指向window 我们在window下给设置一个变量并去掉内部的赋值:
 
JavaScript代码
  1. var x = 1;  
  2. function test(){  
  3.   alert(this.x); //取window下的x  
  4. }  
  5. test(); // 1  
  6.   
  7.   
  8. var x = 1;  
  9. function test(){  
  10.   this.x = 0; //this指针依然是window,只是重新复制而已  
  11. }  
  12. test();  
  13. alert(x); //0  
情况二:作为对象方法的调用
 
作为对象的方法调用时,这时this指针就指这个对象,而不是window!
 
JavaScript代码
  1. function test(){  
  2.  alert(this.x);  
  3. }  
  4. var o = {};  
  5. o.x = 1;  
  6. o.m = test;  
  7. o.m(); // 1  
为了验证以下此时this能不能指向window我们做如下改动:
 
JavaScript代码
  1. var x='windowX';  
  2. function test(){  
  3.  alert(this.x);  
  4. }  
  5. var o = {};  
  6. //o.x = 1;  
  7. o.m = test;  
  8. o.m(); // undefined  -->可见this不可能指向window  
情况三 作为构造函数调用
 
所谓构造函数,就是通过这个函数生成一个新对象(object)。这时,this就指这个新对象。
 
JavaScript代码
  1. function test(){  
  2.   this.x = 1;  
  3. }  
  4. var o = new test();  
  5. alert(o.x); // 1  
同样,为了验证以下此时this能不能指向window我们做如下改动:
 
JavaScript代码
  1. var x='windowX';  
  2. function test(){  
  3.    this.x = 1;  
  4. }  
  5. var o = new test();  
  6. alert(o.x); // 1   -->可见this不可能指向window  
  7. alert(x); // windowX  
情况四 apply调用
 
apply()是函数体的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数
 
JavaScript代码
  1. var x = 'windowX';  
  2. function test(){  
  3.   alert(this.x);  
  4. }  
  5. var o={};  
  6. o.x = 1;  
  7. o.m = test;  
  8. o.m.apply(); //windowX m函数的apply更改了调用对象-->window  
apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为windowX,证明this指的是全局window对象。
 
如果把最后一行代码修改为:
 
JavaScript代码
  1. o.m.apply(o); //1 运行结果就变成了1,证明this又指向了默认的o对象  
练习测试题:
 
JavaScript代码
  1. /******** this指针及变量提升的问题 ********/  
  2. var foo='windowFoo';  
  3. function main(){  
  4.     alert(foo);               //第一个alert,一般规则是先查找当前内部变量有没有,有则取没有找上一层     
  5.     var foo = 2;  
  6.     alert(this.foo);          //第二个alert,  
  7.     this.foo = 3;}  
注明:首先说下window下直接调用main()方法 this==window
 
测试1: var m1=main(); //作为普通函数调用或者直接调用main()
 
第一个alert属于特殊情况,内部有同名变量存在且在后边定义,涉及到了变量提升,因此输出:undefined
 
第二个alert属于查找this指针对象下的属性,this指针指向window固输出:windowFoo 而不是后边的3
 
 
测试1: var m2=new main();  //作为构造函数实例化之后调用
 
第一个alert 输出undefined因为构造函数实例化后this指向该对象,不会变化
 
第二个alert 输出undefined因为this.foo被实例化对象继承后等同于增加一个属性方法,且在alert之后,因此输出:undefined
Tags: ,
第一页 上页 4 5 6 7 8 9 10 11 12 13 下页 最后页 [ 显示模式: 摘要 | 列表 ]