tp框架中的一些疑点知识-8

  1. NaN是Number对象的一个属性, 表示一个特殊值, 表示不是一个 数字, 引用/赋值时, 要使用: Number.NaN
  • 判断 一个值是不是 NaN, 用 isNaN() 函数, 它是一个js的全局函数 , 所以前面不需要用 对象什么的来引用, 直接使用 isNaN() . 同时要注意, 判断一个数是否是NaN不能用 == NaN, 因为NaN, 不等于任何值, 即使是它自身也不相等, 即: NaN != N阿N

  • 注意这个函数不要 想反了. 是判断一个变量(不是一个数字).

  • 当在一个 (任意的)变量(比如: 即使是一个函数表达式 )前面加上 + - 的时候, 就是 (隐含表示 将这个变量转换为数字), 这时候, 如果bu是一个有效的数字(比如在一个函数前加上一个 +/- , ), 就会输出 NaN

  • 如果在一个 (任意的)变量 前面加上 ! 就表示 对后面的内容 按 逻辑值 求反值.(比如: 即使是一个函数表达式)

  • ! 表示求反值的时候, 可以连续使用多个 感叹号! 比如 !!true 还是true, !!false就还是false

  1. prompt,alert, confirm是 js的顶级对象 window的 属性/方法. 所以 你可以省略(当然也可以带上)window. 这三个都是 模式对话框
    prompt("只能是非html的提示text", "defaultvalue") 返回值: 单击cancel时返回 null. 可以通过console.log()来查看.

关于document.write的理解?

  1. js的document.write("任意的html代码, 支持 += 字符串操作符") . 要深刻理解他的作用机制: 向 当前 正在进行的 页面的输出流中 加入 html内容.
  2. 所以, 如果是要在 js的 延后脚本中, 调用document.write的话, 就要注意了: 因为 当文档被 载入 完成后, 那么当前 输出流就被 关闭(销毁)了. 这时候, 如果 再延时执行(不是第一次载入的时候执行) document.write的话, 就必须再重新 打开一个 文档输出流 进行写入, 相当于重新载入一个新的 html内容. 所以, 原来页面上的任何内容(包括变量和脚本) 都将不存在了???? (为什么 原来页面的变量还存在???)

参考: https://www.v2ex.com/t/303750#r_3525935

当你打开一个页面,浏览器会

1. (前面做了很多事情,与本文无关,省略)
2. 调用 document.open() 打开文档
3. document.write(...) 将下载到的网页内容写入文档
4. 所有内容写完了,就调用 document.close()
5. 触发 dom ready 事件( DOMContentReady)


所以你如果在第 4 步之前 document.write(1) 那么你就直接追加内容到当前位置,
如果你在第 4 步之后 document.write(),那么由于 document 已经 close 了,所以必须重新 document.open() 来打开文档,这一打开,内容就被清空了。

不信你可以这样验证一下:

1. 打开 baidu.com 等页面加载完
2. 在控制台运行 document.write(1),会看到页面清空,只有一个 1
3. 再次运行 document.write(1),会发现页面没有清空, 1 变成了 11 ,因为追加了一个 1
4. 运行 document.close(),这时文档就关闭了。
5. 再次运行 document.write(1),你会发现文档又清空了,变成了 1 。

js中获得当前时间 的函数, 每种语言都要 获取 当前时间. 但他们的函数名称都不一样. 只有自己记住了. 比如: php中用time()函数, js中用 内置对象: new Date(); 然后用对应的方法. new Date().getYear();

关于js中的 eval函数

eval: 是 evaluate求值的意思, 它是js的原生函数, 其参数是字符串, 本来字符串是没有 "求值/计算/执行" 功能的, 是literal的. 但是 经过eval函数包装后, 其中的字符串就是 js代码来执行了(先去掉引号, 然后执行没有引号部分的js内容).然后返回值.

  • 相当于 bash中的反引号 的作用
  • 有几种情况: 一是计算其中的算术表达式的值, 并返回; 二是把codestring参数当作命令 来执行; 三是将字符串转为 对象
  • 目的是: 在一些特殊情况下, 更灵活, 更方便 获取索引到 dom(document object module)的对象
  • html元素有id, class, type, value, name等属性, 其中id和class主要是给前端js脚本用的, 而name主要是给后台脚本用的. 要注意, 表单元素 本来 就有 value这个属性.

除了用jquery写 也要应用/不能完全抛弃 原生的js函数. 不能滥用jquery, 因为有些原生的js脚本 更快. 比如事件, 主要的事件要掌握, 像鼠标事件(onclick, ondblclick...) 键盘事件(onkeyup...) body事件(onload: 这个是指整个文档包括include的文档, 载入完成后的事件,不是指 刚载入时的事件,其实应该是onloaded. onresize, onerror, onscroll onstop, onmove等)


如何让表单中的控件 获取js中 的变量值? 比如hidden控件的当前时间值?

  1. 首先你不能直接通过 表单的属性获取 变量值、执行js代码, 因为虽然有 onclick事件属性, 但是你没法单击, 一是不符合操作习惯,二是对于隐藏控件你根本就单击不到嘛。

  2. 那么只有通过 js代码来给hidden控件赋值, 而且是通过 页面载入时就自动执行的 script脚本.

{__NOLAYOUT__}

<form action="{:U('check')}" method="post">
<p>
	user:<input type="text" name="user"  />
</p>
<p>
	passwd:<input type="password" name="passwd"  />
</p>

<p><input type="hidden" name="submit_time" id="htm" value="tm()"></p>   // 这样想获得tm()函数的执行结果是不得行的, 最终是把 tm的内容字符串literal付给value.
								// 实际并不会 获得执行后的值.

<p>
	<input type="submit" />
</p>

</form>

<script>

/*
function tm(){
	document.getElementById('htm').value = new Date().getTime();
}

window.onload = tm;
*/

// 或者前面的都不要, 函数什么的都不用要!  直接就中间的 那一句话 就可以了:

document.getElementById('htm').value = new Date().getTime();  // 注意dom 的根, 是document, 不是window!

</script>
#### 不同的浏览器对 type="submit"的 渲染/显示是不一样的, 比如ff和google的效果就不一样, 所以最好还是 明确地给 submit的value 赋值.
  1. 在form的表单控件中, 一定要写 name属性, 因为这个是给后台action目标页面 的$_GET $_POST用来获取 数据的 名称索引, 如果没有name属性,那么相应的控件的值是获取不到的, 是没有的.

  2. 程序中的时间戳为什么基本上从1970年1月1日23:59开始计算的.
    因为 .... unix系统在1969年成雏形. 因为时间戳主要是用来记录文件时间的. 而在这之前很少有记录文件时间的需求. 所以 包括js, php等的时间戳都是从 1970年1月1日... 开始计算的.

  3. js的时间戳是从1970年1月1日到现在时间(某个给定时间)的毫秒数, 有三种获取方法:

  • 使用Date对象的静态方法parse: var timestamp= Date.parse(new Date()); 这种方法的最后三位毫秒 全部置为000
  • 使用 Date对象的 valueOf()方法 或getTime()方法. 这两种得到的也是毫秒数. 是一样的:
    new Date().getTime() == new Date().valueOf();

verify 校验码的ajax刷新 问题

在检验验证码的时候, 要调用 Verify类 的check函数. 生成类的对象, 不一定 跟检验类的对象是同一个. 因为它们都会 去读写$_SESSION, 所以 读写的其实同一个值, 所以可以的. 但是 由于 同一个页面, 可以允许多个验证码, (虽然一般都不会这样做), 所以可以给验证码指定一个id, 表示 是第几个验证码. 那么, 在 check的时候, 就要把对应的id值 传递过去.


关于 验证码的检验问题?

  • 注意 密码 类型的input, type必须是 password 才有效, 不能是 passwd.

  • 在 minibufferExplorer中, buffer是按顺序进行排列的, 也就是 紧挨着的 两个buffer 之间可以用 bp 和 bn进行切换.

  • 而即使不是紧挨着的两个 buffers 之间, 即最近的两个buffers之间 是可以 用 ctrl +6 来切换的.


  1. 为什么不能用 getElementByName来获取元素?
https://blog.csdn.net/ghostyusheng/article/details/50449428 这个问题的根源实际上还是HTML代码写的不规范引起的,一直以为是js的问题,实际上却忘了检验HTML的规范性,HTML标签有的有name属性,比如input、select、button等,而很多是没有的,比如td、div等,只有有name属性的标签才可以使用getElementsByName方法。

ps:name属性,name属性是input标签的内建属性,早期浏览器的getElementsByName方法是为了  **方便的获取用户的输入**。由于name只是input的内建属性,其它标签没有,所以getElementsByName方法不能在别的标签中识别这一属性,因此getElementsByName方法只能用于 **(在表单form中的 ) input标签,这也** 就是为什么你getElementsByName(‘ok’)得不到任何值的原因。
  • 可以有多个具有相同id的元素, 那么实际起作用的只是第一个元素, 其他都是 "伪id"
  • 如果只是有一个 要获取的元素, 建议 使用id, getElementById
  • 不管是对form表单中的元素, 还是非表单中的元素, 都可以使用; getElementByTagName(...)
  • 通常对于多个具有 相同name的元素, 获得的是一个 数组元素, 所以要判断 getElementsByName('certainName').length是否不为0

一直有个问题其实是错误的理解, 就是通过name获取 表单form中的input 标签的value时, 使用的函数名是: getElementsByName('some-name'), 不是 getElementByName. ` 注意, 这里的elements是复数, 不是element的单数, 否则, js会报错: getElementById不是一个函数!

  • 而且, 对于getElementsByName 可以对 hidden类型的input控件也可以获得对应的dom节点

占用用户的cpu,其实现在用户的cpu最少都是1.5GHz以上,这点小运算对CPU来说根本就是骆驼身上的一个细胞,而且用户的cpu闲着也是闲着,既然对他供着电,那就让他做点事情吧.


当你对面向对象, 面向类 的思想和方法有着深刻的认识和理解后 , 你自己都能设计/写类库了, 那么你使用别人的类就很容易了. 而且, 不同的人, 写的同一个功能的类, 由于设计的水平/习惯/方式不同, 里面的构造函数,调用函数, 传参等的 方式, 传递参数的时期 也很可能不一样. 这就决定了你使用它的类的时候, 的写法也就不一样.

  • get_client_ip是一个common/fucntions.php中的全局函数, 实际上是一种封装, 对$_SERVER服务器的信息来的.
  • 上面的只是一个ip, 而要根据ip得到用户的实际地理位置, 就要通过 IpLocation这个类来写.


php类的问题

  • 虽然php的私有数据, 可以通过__set, __get来 动态设置和 获取, 但是不管从书写多少还是从执行效率来说, 都应该用 __construct构造函数在 初始化创建对象的时候, 传入自定义的配置参数(或数组) 这样更符合c/c++的写法.
    public function __set($name, $value) {
	    if(isset($this->name)){
	        $this->name = $value;	
	    } elseif (isset($this->cfg[$name])){
	        $this->cfg[$name]	= $value;
	    }
    }

    public function __get($name){
	    return isset($this->name)?$this->name:$this->cfg[$name];   // 这些数组中的 下标变量 都不 要 加  引号! 
    }

  • 私有成员的 定义 方式有两种, 一种是 单独的, 一个一个的 分离起来 写的; 另一种是 合起来 放在 配置 数组 成员变量中的. 两种方式都可以, 看你的 类操作数据 的 需求和方式来定. ** 要注意的是, 这两种方式, 在 引用上 的写法 是一样: 都可以用 $this -> memberName来 引用, 后一种数组方式, 也不必用: \(this -> config['memberName') 来引用: 因为 你写的 __get(\)name) 就是配置数组中的成员 : {return $this->config('memberName'); **

  • php的 && 和 || : 不仅仅只是在 if条件判断中使用, 还有 更简洁的 使用方式: 直接 在 执行语句中写. 比如: $this->reset && session($key, null);

比如:

<?php
namespace Common\General;

class Car {
    protected 




}

<?php
return array(
	//'配置项'=>'配置值'
	'AUTOLOAD_NAMESPACE' => array(
	   'General'  => COMMON_PATH.'General', 
	),
);
  1. 在php中类的成员变量, 叫做: private/protected?public property(财产/属性), 只要你写了 __get魔术方法, 即使没有任何函数体语句, 那么你访问private/protected变量都不会出错. 因为实际上经过了这个魔术方法的处理了

  2. Nerdtree的 关闭快捷键: 不是c, 而是x, 为什么是x? 可能是 根据 "通常 /所有的 窗口的 关闭 按钮上都是 写的: x " 这个习惯来的. 小写的x, 表示 关闭当前目录 折叠 到父目录. 那么 相反的 字母 X就是 关闭 当前目录下的 所有 已经打开的子目录

  3. 很重要的一个要点是: 对于数组元素, 索引名称 通常是用字符串 来表示, 但是 ,如果 索引名称 用 变量来表示的话, 由于 变量本来就已经 是 一个字符串了, 所以, 这时候, 数组引用的 时候, 就不要/不能 再在 变量的两边 加 任何引号了, 这时候, 如果加上引号, 反而会出错, 只有 在 echo的 时候, 才需要 在 变量 的两边加 单/双 引号.

if(isset($this->config[$name])){
    $this->config[$name] = $value; 
}

要排除tp中的验证码的错误, 就要对Verify类的源码进行分析

  1. 在 "绘制" 验证码的时候, 是一个字符一个字符 来进行绘制的, 所以, 生成的$code 就是一个数组. $code=array();
  • 那么对验证码 进行加密的时候, 就要先把 数组 转换为字符串, 所以 用了 implode('', $code);
  • tp的验证码,是不区分大小写的, 原因就只有一个, 在 调用 类的加密验证码方法 authcode 的时候, 先对字符串进行了 大写转换, 然后检查的时候, 调用check 方法的时候, 也对 传入的字符串参数进行了 大写转换的. $code = $this -> authcode(strtoupper(implode('', $code)));
  1. 在authcode中 要注意区分几个概念和变量名称(自己就这么约定)
  • $key是 $_SESSION (这里是一个二维数组, 因为可能有多个验证码) 每个验证码的 名称索引;

  • $secode 是 $_SESSION的 每个索引 所对应的 验证码信息, 它也是一个数组, 包括两个元素: 元素的索引名称分别是: verify_code, verify_time

  • \(code 则是每个\)secode中的 verify_code对应的元素值了.

  • 而 $key 和 \(code 都是 由 `\)this -> authcode($code) ` 加密 而来的.

  • 因此, $_SESSION 的样子大概就是这样的了:

$_SESSION = array(
	$key1 => $secode1, 
	$key2 => $secode2,
	.....

);

其中 $key1  是由 $key 和 验证码的id组合而成的, 即: $key .$id
$secode1 = array ('verify_code' => $code, 'verify_time' => NOW_TIME); 


authcode 是怎样进行加密的?

   /* 加密验证码 */
    private function authcode($str){
        $key = substr(md5($this->seKey), 5, 8);
        $str = substr(md5($str), 8, 10);
        return md5($key . $str);
    }
  • 实际上,进行 了三次 md5的加密

  • 里面有两个变量名称, 一个是 seKey : 称为"加密密钥"(这个是 类Verify的数据成员 ), 这个最好是 自己改一下, 不要用 tp的默认值; 第二个是 authcode的参数 $str, 称为"被加密字符串".

  • 三次md5: 第一次 对 加密密钥$this->seKey 应用md5加密(这个是固定不变的, 不管你传递 什么 $str 都不变); 第二次 是对 "被加密字符串" $str 应用 md5; 第三次 是 对 前两次加密结果 连接起来的 结果 再进行一次md5 处理.

  • 实际上, 上面的 验证码生成后, 写入到$_SESSION 中, 其中的 每个验证码的 索引名称 也是 用 authcode 加密得到的: 以类的 成员变量 seKey这个加密密钥 作为 被加密字符串 传递到 authcode方法后生成的 结果 + 加上 验证码的id 号

        // 保存验证码
        $key        =   $this->authcode($this->seKey);
        $code       =   $this->authcode(strtoupper(implode('', $code)));
        $secode     =   array();
        $secode['verify_code'] = $code; // 把校验码保存到session
        $secode['verify_time'] = NOW_TIME;  // 验证码创建时间
        session($key.$id, $secode);

关于tp的verify的 check函数?

  • $id 参数的默认值 等于空, 因为在 生成entry 和 验证的时候, 都是设置的 空

  • // 要去获取 $_SESSION 中的验证码, 首先要获得索引名称, -> // 要去获取 $_SESSION 中的验证码, 首先要获得索引名称 , 然后判断: (这里只判断了3 种情形, 但是也有可能有其他情形没有被包含, 所以要 默认的 返回false)

  • 第一种情形, 如果是 传入的字符串为空, 或 $_session中的 验证码为空, 则false;

  • 第二种情形, 如果验证码 过期: 用当前时间 - 验证码的生成时间, 如果 大于 设置的 过期时间 (tp的默认过期时间是 1800s 半小时), 则false, 并且要删除当前这个 验证码. 注意 在tp的帮助文档中说了, 删除某个 session, 使用的是 session($name, null), 实际上这个 $name就是 session 的 索引名称.

  • 第三种情形, 就是 验证成功. // 也是 对传入的参数 进行 大写转换后, 进行authcode 处理为md5 后进行比较的. $this->reset && session($key, null) ; 的解释是:

 public function check($code, $id = '') {
        $key = $this->authcode($this->seKey).$id;  
        // 验证码不能为空
        $secode = session($key);    

        // 第一种情形
        if(empty($code) || empty($secode)) {   
            return false;
        }

        // session 过期
        if(NOW_TIME - $secode['verify_time'] > $this->expire) {
            session($key, null);
            return false;
        }

        // 第三种情形
        if($this->authcode(strtoupper($code)) == $secode['verify_code']) {    
            $this->reset && session($key, null);
            return true;
        }
        
        // 默认没有提到的情形
        return false;
    }

posted @ 2018-05-15 08:41  noitanym  阅读(344)  评论(0编辑  收藏  举报