JavaScript的使用建议

JavaScript的使用建议

  1. 使用===代替====!=会强制转换类型。
  2. 避免使用evaleval不仅降低脚本的性能,而且由于需要的权限比较高会造成安全风险。
  3. 不要省略{},即使代码快只有一行也不可以,便于以后的阅读和修改。
  4. 使用JSLint检查代码是否存在错误。
  5. 把js脚本嵌在文件的底部。
  6. 避免在for语句中声明变量。
  7. 使用数组或者对象构建字符串时使用join方法。
  8. 脚本中减少全局变量的使用,建议用对象把方法和属性包裹起来。
  9. 给代码添加注释。
  10. 在没有启用js的情况下逐步增强网站效果。
  11. setIntervalsetTimeout不要传递字符串参数而是使用函数名称。
  12. 不要使用with语句。
  13. 使用{}代替new Object()
  14. 使用[]代替new Array()
  15. 定义多个变量时,省略关键字letvar等,用逗号代替。
  16. 不要省略分号。
  17. for in使用判断条件过滤信息避免多余的循环次数以提高性能。
  18. 通过计算运行时间优化代码。
  19. 多阅读。
  20. 自执行函数,用(函数实现)(函数参数);实现自执行函数。
  21. 移除script标签中的language属性。

js学习(一)

setState参数

1
2
3
4
void setState(
function|object nextState,
[function callback]
)
  1. 第一个参数可以是一个函数也可以是一个对象,但是返回值都是下一个state的状态。
  2. 第二个参数为setState这个函数异步调用结束以后的回调函数,可以用在组件设置完state以后进行的操作。

js对网页滚动条的操作

  • js可以通过改变目标元素的scrollTop改变元素滚动条的位置
  • onWheel事件可以监听滚动条滚动事件,可以通过deltaXdeltaY属性获取滚动条滚动的位置和方向
  • onScroll事件可以监听滚动条事件,但是无法获取方向和滚动距离及位置
  • CSS中没有可以操作scrollTop值的属性

setInterval

setInterval定时执行函数,当先后创建多个定时器时可能不会按照你预想的顺序来执行,因为js是单线程的,只有当前一个任务执行完毕以后才能够继续执行下一任务,所以调用定时器的时间可能大于你预设的时间,由此造成调用定时器的混乱。

js实现动画

  • 通过js中的定时器不断的改变元素的位置和高度模拟动画效果
  • 通过一个定时器管理函数管理定时器的先后调用顺序或者通过dom2级时间removeEventLisenner函数让元素在js调用定时器期间失去时间响应解决定时器无法顺序调用的各种问题

运算符重载

C++中预定义的运算符的操作对象只能是基本数据类型。但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作。这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作。运算符重载的实质是函数重载,它提供了C++的可扩展性,也是C++最吸引人的特性之一。

运算符重载是通过创建运算符函数实现的,运算符函数定义了重载的运算符将要进行的操作。运算符函数的定义与其他函数的定义类似,惟一的区别是运算符函数的函数名是由关键字operator和其后要重载的运算符符号构成的。运算符函数定义的一般格式如下:

    <返回类型说明符> operator <运算符符号>(<参数表>)
    {
        <函数体>
    }

运算符重载规则

  • 除了类属关系运算符”.”、成员指针运算符”.*”、作用域运算符”::”、sizeof运算符和三目运算符”?:”以外,C++中的所有运算符都可以重载。

  • 重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。

  • 运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循函数重载的选择原则。

  • 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构。

  • 运算符重载不能改变该运算符用于内部类型对象的含义。它只能和用户自定义类型的对象一起使用,或者用于用户自定义类型的对象和内部类型的对象混合使用时。

  • 运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造,重载的功能应当与原有功能相类似,避免没有目的地使用重载运算符。

运算符函数重载一般有两种形式:重载为类的成员函数和重载为类的非成员函数。非成员函数通常是友元。可以把一个运算符作为一个非成员、非友元函数重载。但是,这样的运算符函数访问类的私有和保护成员时,必须使用类的公有接口中提供的设置数据和读取数据的函数,调用这些函数时会降低性能。可以内联这些函数以提高性能。

成员函数运算符

运算符重载为类的成员函数的一般格式为:

    <函数类型> operator <运算符>(<参数表>)
    {
        <函数体>
    }

当运算符重载为类的成员函数时,函数的参数个数比原来的操作数要少一个(后置单目运算符除外),这是因为成员函数用this指针隐式地访问了类的一个对象,它充当了运算符函数最左边的操作数。因此:

  • 双目运算符重载为类的成员函数时,函数只显式说明一个参数,该形参是运算符的右操作数。

  • 前置单目运算符重载为类的成员函数时,不需要显式说明参数,即函数没有形参。

  • 后置单目运算符重载为类的成员函数时,函数要带有一个整型形参。

调用成员函数运算符的格式如下:

    <对象名>.operator <运算符>(<参数>)

它等价于

    <对象名><运算符><参数>

例如:a+b等价于a.operator +(b)。一般情况下,我们采用运算符的习惯表达方式。

友元函数运算符

运算符重载为类的友元函数的一般格式为:

    friend <函数类型> operator <运算符>(<参数表>)
    {
        <函数体>
    }

当运算符重载为类的友元函数时,由于没有隐含的this指针,因此操作数的个数没有变化,所有的操作数都必须通过函数的形参进行传递,函数的参数与操作数自左至右一一对应。

调用友元函数运算符的格式如下:

    operator <运算符>(<参数1>,<参数2>)

它等价于

    <参数1><运算符><参数2>

例如:a+b等价于operator +(a,b)。

两种重载形式的比较

在多数情况下,将运算符重载为类的成员函数和类的友元函数都是可以的。但成员函数运算符与友元函数运算符也具有各自的一些特点:

  • 一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。

  • 以下一些双目运算符不能重载为类的友元函数:=、()、[]、->。

  • 类型转换函数只能定义为一个类的成员函数而不能定义为类的友元函数。

  • 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。

  • 若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数。

  • 当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一 个类对象(或者是对该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部 类型的对象,该运算符函数必须作为一个友元函数来实现。

  • 当需要重载运算符具有可交换性时,选择重载为友元函数。

memcpy函数的实现

memcpy的代码实现

void * memcpy(void *dest, const void *src, size_t count)
{ 
        if (dest == NULL || src == NULL)//判断是否为空避免非法操作
        return NULL; 
        char *pdest = static_cast <char*>(dest);
        const char *psrc  = static_cast <const char*>(src); 
        int n = count; 
        if (pdest > psrc && pdest < psrc+count) //目的地址大于源地址且存在内存重叠
        { 
            for (size_t i=n-1; i != -1; --i)//从高位开始复制避免出现字符覆盖的情况
            { 
                    *(pdest+i) = *(psrc+i); 
            } 
        } 
        else
        { 
            for (size_t i= 0; i < n; i++) //从低位开始复制
            { 
                   *(pdest+i) = *(psrc+i);
            } 
        }
        return dest; 
}
注:这里没有考虑内存重叠的所有情况

strcpy的实现

不考虑内存重叠

char * strcpy(char *dest,const char *src)
{
    char *ret;
    if(dest==NULL || src==NULL)//检查是否为空指针
        return NULL;
    while((*dest++=*src++)!='\0');//复制字符串中的内容
    return ret;
}

考虑内存重叠

char *strcpy(char *dest,const char *src)
{
    char *ret=dest;
    if(dest==NULL || src==NULL)//检查是否为空指针
        return NULL;
    memcpy(dest,src,strlen(src)+1);//调用memcpy函数避免内存重复的情况
    return ret;
}

说明

  • const修饰源字符串避免对源字符串的修改
  • 检查指针的有效性避免非法操作
  • 返回目标字符串的首地址确保字符串的线性
  • 使用(*dest++=*src++)!='\0'确保字符串的末尾有结束符

计算机网络-TCP链接的建立与释放

TCP 链接的建立(三次握手)

  1. 服务器启动之后会持续监听连接请求
  2. 客户端主动发起建立连接的请求,其请求中包含 SYN 标志位和一个报文客户端序列号 seq=x
  3. 服务端收到建立连接的请求时,主动进入 SYN 已收到的状态,同时返回一个确认报文,报文包含 SYNACK 状态位,同时返回确认序列号 ack=x+1 和当前报文的服务端序列号 seq=y
  4. 客户端收到确认报文之后进入连接已经建立状态,然后立即返回一个确认报文,报文包含 ACK 状态位,同时返回确认序列号 ack=y+1 和当前报文的服务端序列号 seq=x+1

TCP链接建立完整示意图

  • TCP 规定,SYN 报文段(即 SYN=1 的报文段)不能携带数据,但是要消耗掉一个序号;ACK 报文段可以携带数据,如果不携带数据则不消耗序号。
  • 流程中每一个报文若未收到响应,发送端都会重复发送几次
  • 服务端进入 SYN 已经收到状态且已经发送响应时会启动超时定时器,超时未收到响应时就会释放相应的资源
  • 部分服务器为了避免 TCP 连接攻击,会在收到客户端确认响应之后才会分配相应的资源

TCP 链接的释放(四次挥手)

以客户端主动断开连接为例

  1. 客户端发送端开连接的请求报文,报文包含 FIN 状态位和客户端序列号 seq=u,并进入发送通道关闭等待响应状态
  2. 服务端收到报文后会通知上层的应用程序,同时返回确认报文,报文包含 FINACK 状态位,以及确认序列号 ack=u+1 和当前报文的服务端序列号 seq=v
  3. 客户端收到响应报文之后进入发送通道关闭状态,同时进入接收通道等待关闭
  4. 服务端发送完所有数据之后会发送连接关闭报文,此报文包含 FINACK 状态位,以及确认序列号 ack=u+1 和当前报文的服务端序列号 seq=w,进入连接关闭等待确认状态
  5. 客户端收到接收通道关闭请求报文后,等待一段时间后关闭接收通道,同时发送一个确认报文给服务端,此报文包含 ACK 状态位,同时返回确认序列号 ack=w+1 和当前报文的服务端序列号 seq=u+1
  6. 服务端收到请求后关闭发送通道
  7. 客户端等待时间过期之后关闭连接

TCP链接释放完整示意图

  • TCP 规定,FIN 报文段即使不携带数据也要消耗一个序号。
  • 四次挥手是为了确保对方发送的数据发送完毕,全双工通信只能确保主动关闭的一方无数据发送,对端可能还有数据需要发送
  • 客户端收到关闭请求不会立即进入关闭状态而是等待一段时间后再关闭是为了等待网络中滞留的报文