Js关于this和事件冒泡的一些总结

整  理:zhangyue
日  期:2017.1.6
说  明:关于js的this和事件冒泡等的部分总结

一 Js中this和Jquery中$(this)的区别
1.1 两者是不同的对象
$(this) :jquery对象
this:dom对象

1.2 dom对象和jquery对象的转换:

$(this)[0] == this; 

var $argument=$("#argument"); //jquery对象
var argument = $argument[0]; //dom对象 也可写成 var argument=$argument.get(0);

var argument=document.getElementById("argument"); //dom对象
var $argument = $(argument); //转换成jquery对象

二 Js事件冒泡和事件委托
事件冒泡:子级元素的某个事件被触发,它的上级元素的该事件也被递归执行
事件委托:将子级元素的事件绑定在父级元素上;
对于动态生成的Dom元素,其事件绑定有两种方式:第一种,是在dom元素后生成的地方添加相应的绑定事件;第二种,将事件绑定在已经存在的父元素dom节点上。如果一开始就对该元素进行绑定事件,而该元素动态添加后,是不能够响应改绑定事件的。




//js 部分
createSonDom();

//第一种方式
$("#son").click(function(){
    alert('son');
});

//第二种方式 事件委托
$("#parent").on('click', '#son', function() {
    alert('son');
});

function createSonDom()
{
    var tmp = [];
    tmp.push('
'); $("#parent").append(tmp); //第三种方式 $("#son").click(function(){ alert('son'); }); } 第一种方式对于son元素无法响应,第二种可行,第三种可行。第三种存在缺陷,可能会导致事件多次响应

三 Js中this和event对比
js中事件是会冒泡的,所以this是可以变化的,
event.target不会变化,它永远是直接接受事件的目标DOM元素;

$(this)和event.currentTarget永远指向监听该事件发生的DOM元素;
event.target则是那个被点击的DOM元素.

四 使用this的冒泡问题解决
4.1 第一种:事件委托,不把事件绑定在自身元素上
4.2 off(),解除绑定事件,之后在进行绑定

$(this).off('click').on('click', function(){
    alert('off');
});

4.2 使用event.stopPropagation()
能阻止父元素的click事件函数执行,但不阻止当前元素同时绑定的其它click事件函数执行
ps:event.preventDefault() 不组织冒泡事件,阻止默认行为

$('#son').on('click', function(event){
    $(event.target).stopPropagation();//阻止事件冒泡
});

js中==运算

js类型的科普

1.js的值可以分为2种类型,基本类型和对象类型
2.基本类型包括:Undefined、Null、Boolean、Number和String等五种
3.其中Undefined类型和Null类型的都只有一个值,即undefined和null;Boolean类型有两个值:true和false;Number类型的值有很多很多;String类型的值理论上有无数个

类型转换图
类型转换图
ps:N表示tonumber转化为数字比较,P表示转为基本类型比较

多情况比较
一、有和无

1.如图所示,很显然,左边表示的都是确定的,有和非空的,而右侧就是不确定的,无,或者空的,很显然两者==作比较是false的

二、空和空

1.undefined和null是比较难以区分的点
首先我们看下两者的类型

alert(typeof undefined); //output "undefined"
alert(typeof null); //output "object"
前者很容易理解undefined的类型是undefined
后者你会想为什么null的类型是object有点无语,看别人解释是说这是一个最初设计的问题
alert(null == undefined); //output "true"
为什么会是true呢,null表示不存在对象,该出不应该有值
而undefined是该处缺少值,没有赋值
那如何区分两者呢(1.===;2.根据类型比较)
三、真与假

Boolean类型的值,与其他值作比较的时候,会转化为1和0

四、字符的序列

string和number分在一起是因为它们都是字符的序列,数字可以当做一种特殊的字符串
字符串和数字作比较的时候,会把字符串转化为数字的形式,看是否是一个合法的数字比如 123->123;123abc->NaN;\r\n\t123\v\f->0;

五、对象与基本类型

概括一下,对象是各种属性的集合(而属性又可以是对象),所以对象是复杂度是无限制的
而一般我们使用toString来得到关于对象的文字描述(通常是字符串);用valueOf获取对象的原始值;
根据图1,当一个对象与一个非对象比较时,需要将对象转化为原始类型;
var str = new String('hello,world');
console.log(typeof str.toString()); //string
console.log(typeof str.valueOf());//number

六、自身比较

基本类型的比较,如果2个值相同,则为true,而对象比较特殊,只有地址相同才相等。当比较两个引用值时,比较的是两个引用地址,看它们引用的原值是否为同一个副本,而不是比较它们的原值字节是否相等。

数据库索引浅析

数据库索引

众所周知,索引是用来提高sql语句的查询效率的,那么索引到底是什么,我们该在什么时候建立索引,该如何取建立一个好的索引呢?

索引的原理

索引是一种数据结构,来看一下几个最基本的算法,比如我要查找一个id = 8888的用户,
select * from users where id=8888 limit 1;
首先想到的就是顺序查找,直到查到一条id为1 的元素为止,显而易见,这个算法的复杂度是O(n),是很糟糕的,理论上当n越大,开销越大。进而想到有些比较好的算法,二叉树,二分查找,但是都是有要求的,二叉树只能针对二叉树的数据结构起作用,二分查找也只能对有序的数据结构起作用,而数据库本身可以维护着一套符合特定算法的数据结构,可以用某种方式指向数据的数据结构,而我们使用的mysql采用的是B+树的方式建立所索引的。

a4a89204-44ec-38f0-a011-dad4f05acfdf
如图所示,是一棵高度为2的B+树,也可以很清晰的看到其特点
1.所有的记录都存放在叶子节点
2.所有的数据都按顺序排列(到最后通过二分查找获取数据)
3.叶子结点通过双向链起来,便于定位插入新的叶子结点
为什么说它优秀呢?通常数据库索引的消耗来源于磁盘io,而B+树,一个叶子节点对应着一个页,理论上,只需要读取m次(树的高度)磁盘,就可以得到数据所在节点

当产生一个新的元素,存在两种情况
1.插入所在页的叶子结点没满,直接插入
2.当叶子结点已满,会将此叶子结点一分为二,在父节点增加一个新的结点,因此这种方式不会影响兄弟结点

索引的类型

1.普通索引
正常的索引,没有限制
2.唯一索引
与普通索引类似,不同的就是:索引列的值必须唯一,如果是组合索引,则列值的组合必须唯一,创建方法和普通索引类似。
坑:如果一开始没有想好该字段建唯一索引后,由于表中已经存储了大量的数据,这时候就需要考虑去除重复项的事情了,而唯一索引有条语句
alert ignore table wx_fans add unique index(openid)是可以帮你省却很多操作的
3.全文索引
与普通索引不同,适合于大字段处理,举个例子,搜索一段文字,你不需要把全文都输入查询,只需要输入关键字
4.组合索引
较为复杂点,多经常有多个限制条件的sql语句适用,比如搜索来源为4,访问途径是2的用户
select user_id from users where from=4 and mtype=2 limit 100;
这时候可以建立 (from,mtype)的组合索引,比2个索引分开建立更有效率,但需要注意的是mysql有最左前缀的组合,所以只有from,mtype或者from查询的时候才能使用索引,而单mtype是无法享受的

什么情况建立索引?

1.对经常使用where查询的字段设置索引
2.数据库不要出现null值,因为索引是无法检索null值所在的列的
3.短索引(没用过),意思就是去某个字段的前几个字符建立索引
4.对数据量很大,但是重复值很多的不要去建立索引
5.索引的数据类型最好是简单而又小的,比如int型

工厂模式在实际中的应用

前言:为什么要使用设计模式?
设计模式并不是生来就有的,也是前人大量编码,最后总结出来的一套代码结构设计经验,根本目的也是为了增加代码的复用性和可维护性。
顺便说一下设计模式很看重一个原则->开闭原则,会在下面的示例中得到体现!简单的来说就是说对扩展开放,对修改关闭。

工厂模式
工厂模式是一种实例化对象的模式,是用工厂方法代替new操作的一种方式
工厂模式根据抽象的程度,可以分为简单工厂模式,工厂方法模式,抽象工厂模式。
Ps:对于一些功能相似的类,我们通常会提供一个抽象接口,把公共方法暴露出来,供调用者实现具体的逻辑。
示例讲解

以保险为例

1.简单工厂模式
由一个工厂类根据传入的参数决定创建哪一种产品的类,其实属于一种比较自然的再封装,我们通常也会在代码设计中不经意的使用

interface Create_product {
    public function getProducts();
}
//xyz的产品类
class Product implements Create_product
{
    public fucntion getProducts()
    {
        //todo get products by api
        echo 'get products successfully';
    }
}
//anlian的产品类
class Product implements Creat_product
{
     public fucntion getProducts()
    {
        //todo get products by api
        echo 'get products successfully';
    }
}
class Simple_facrtory {
    public static function createProduct($supp_name)
    {
        if ($supp_name=='Xyz') {
            return new  Xyz\Product;
        }
        if ($supp_name=='Anlian') {
            return new Anlian\Product;
        }
    }
}
$product = Simple_factory::createProduct('Xyz');
$product->getProducts();

优点:客户端免除了具体产品对象的创建,统一使用工厂去创建对象
缺点:违背了开闭原则,当需要有新的产品的时候,会在工厂类的基础上修改,很容易引发错误。

2.工厂方法模式
对简单工厂模式进行了抽象,有一个简单抽象工厂类负责制定以下规范,一般在这种模式中,工厂类会与某个具体的产品类一一对应。

//抽象产品类的方法
interface Create_product {
    public function getProducts();
}
//xyz的产品类
class Product implements Create_product
{
    public fucntion getProducts()
    {
        //todo get products by api
        echo 'get products successfully';
    }
}
//anlian的产品类
class Product implements Creat_product
{
     public fucntion getProducts()
    {
        //todo get products by api
        echo 'get products successfully';
    }
}
//将对象的创建抽象成一个接口
interface Create{
    public function product();
}
class FactoryXyz implements Create{
    public function product()
    {
        return new Xyz\Product;
    }
}
class FactoryAnlian implements Create{
    public function product()
    {
        return new Anlian\Product
    }
}
$factory = new FactoryAnlian();
$product = $factory->product();

优点:实现了开闭原则,对于新增的产品,只需要新增对应的产品类和工厂类,不需要对已有代码的修改,降低修改的风险
缺点:对于不同种类的产品,表示无能为力

3.抽象工厂模式
先举个例子,通过上面2个示例,我们制造这个产品了,但是我们有产品后,又需要去搞订单了,是不是又要创建另外的一个工厂类专门来处理订单呢?显然是不合适的,这时候,我们就会想,是不是可以把相关联的产品(产品和订单)放到一个工厂里,也就没必要再新开一个工厂了!

interface Create{
    public function product();
    public function order();
}
class FactoryXyz implements Create{
    public function product()
    {
        return new Xyz\Product;
    }
    public function order()
    {
        return new Xyz\Order;
    }
}
class FactoryAnlian implements Create{
    public function product()
    {
        return new Anlian\Product;
    }
    public function order()
    {
        return new Anlian\Order;
    }
}
$factory = new FactoryXyz();
$product = $factory->product();

优点:抽象工厂模式,核心在于'一系列', 单个工厂可以生产多个相关的产品。便于在业务中切换工厂。
缺点:显而易见,当出现新的产品的时候,改动开销就很大了,需要在每个工厂类中都加入对应的产品。比如说新增了一个用户,那么就需要在这每个工厂里都加一个用户的实现了,也就是无法支持新增产品,但可以支持多个不同的工厂!

总结:这三种模式并不是说哪个一定好,具体的使用要根据对应的环境来决定的!

CodeIgniter数据库操作


非原创,来源:https://pjf.name/post-383.html

  1. 查询数据
    $query = $this->db->query('select * from xxx');
    /**

    • 获取多条数据
      */
      $res = $query->result();//返回一个对象数组(多维)OR空数组,同$query->result_object();
      $res = $query->result_array();//返回一个关联数组(多维)OR空数组
      /**
    • 获取某条数据
      */
      $res = $query->row();//返回第一行数据(对象方式)
      $res = $query->row(n);//返回第n行数据,如果n大于数据的条数,则返回第一条的数据
      $res = $query->row_array();//同$query->row(),只是返回的数据为关联数组
      $res = $query->row_array(n);//同$query->row_array(),返回的是关联数组
      $res = $query->first_row();//第一条记录
      $res = $query->first_row('array');//返回第一条记录(用数组形式)
      $res = $query->last_row();//最后一条记录
      $res = $query->last_row('array');//最后一条记录(用数组形式)
      $res = $query->next_row();//下一条记录
      $res = $query->next_row(‘array');//下一条记录(用数组形式)
      $res = $query->previous_row();//上一条记录
      $res = $query->previous_row('array');//上一条记录(用数组形式)
  2. 辅助函数
    【查询】
    $query->num_rows(); //查询结果集的行数
    $query->num_fields(); //返回当前请求的字段数目(即列数)
    $query->free_result(); //释放结果集
    【其它】
    $this->db->insert_id();//执行数据插入时的ID
    $this->db->affected_rows();//影响的行数
    $this->db->count_all('table_name');//返回某个表的总行书
    $this->db->platform();//数据库平台(mysql,MS SQL...)
    $this->db->version();//数据库版本
    $this->db->last_query();//最近一次查询
    $this->db->insert_string('table_name' , $data); //生成标准的insert语句
    $this->db->update_string('table_name' , $data); //生成标准的update语句

  3. 原文更正:
    where条件里写大于、小于、不等等符号的时候,要留空格。
    误:$this->db->where('start_time!=','2013');
    正:$this->db->where('start_time !=','2013');

CI框架3.x部分新增特性

  1. CI框架从3.0开始,输入类Input开始支持用数组的方式去取变量值。例如:
    以前(2.x):
    $user_id = $this->input->post('user_id', true);
    $nickname = $this->input->post('nickname', true);
    $version = $this->input->post('version', true);
    ...
    $address = $this->input->post('address', true);
    改进(3.x):
    $keys = array('user_id', 'nickname', 'version');
    list($user_id, $nickname, $version, $address) = $this->input->post($keys, true);

  2. 使用 input_stream() 来获取php://input流数据,例如 PUT 请求
    以前:
    $rsv = file_get_contents("php://input");
    $data = json_decode($rsv,1);
    $user_id = intval($data['user_id']);
    $nickname = stripSql($data['nickname']);
    $version = stripSql($data['version']);
    改进:
    $keys = array('user_id', 'nickname', 'version');
    list($user_id, $nickname, $version, $address) = $this->input->input_stream($keys, true);

  3. 在http和https的兼容上面,$this->config->base_url() 添加了第二个参数 $protocol,可以传入http/https。如果传入【""】,会得到//开头的链接,表示自适应。
    示例:
    $CI =& get_instance();
    $url = $CI->config->base_url("yilucaifu/buy/1024", "");
    /**
    * window.location="//shop.ci123.com/flashsale/yilucaifu/buy/1024";
    */

  4. get(), post(), get_post(), cookie(), server(), user_agent()在没有取到数据的时候,返回NULL而非FALSE。
    意义:
    在以前(2.x),$p = $this->input->get('p', true); isset($p) 始终为真;到3.x后,如果没有p参数,isset($p)为假。

  5. BASEPATH, APPPATH 被定义为了绝对路径,在脚本里可以直接使用这些变量了。

  6. CI 3.x原生支持composer。新建composer.json,填写内容后update就可以使用了。

Js 中的数组和对象

示例一:
var x = [1,2];
x.k = 'v';
console.log(x);
console.log(x.length);
结果是:
[1, 2, k: "v"]
2

示例二:
var x = [1, 2];
x.get = function(){
alert('function get');
}
console.log(x);
console.log(x.length);
结果是:
[1,2] --注释1
2
注释1:展开后是:
0:1
1:2
get:function()

示例三:
var x = new Object();
x[0] = 1;
x[1] = 2;
x.get = function(){
alert('function get');
}
console.log(x);
console.log(x.length);
结果是:
Object {0: 1, 1: 2} --注释2
undefined
注释2:展开后是:
0:1
1:2
get:function()

示例四:
var m = [];
var n = new Object();
console.log(m instanceof Array, m instanceof Object);
console.log(n instanceof Array, n instanceof Object);
结果是:
true true
false true

【结论1】:从上面可以看出来,JS中的数组仅仅是一种特殊的对象,对象和数组都可以具有属性和方法。数组具有 length 属性,而对象没有。当数组具有属性或方法时,属性和方法不会让数组的 length 增加。

示例五:
var p = [];
console.log(p.length);
p[100] = 1; --注释3
console.log(p.length);
结果是:
0
101
注释3:或者p['100']

示例六:
var p = [1,2,3,4,5]
console.log(p, p.length);
p.length = 3;
console.log(p, p.length);
p.length = 5;
console.log(p, p.length);

结果是:
[1, 2, 3, 4, 5] 5
[1, 2, 3] 3
[1, 2, 3] 5

【结论2】:当值被分配给大于数组本身长度的索引时,length 会自动增加到新的长度;当 length 变小时,索引超过数组长度的元素将被删除。(恢复length也无法恢复数组元素)

Js小知识点——setTimeout

一:下面看一个腾讯面试题,主要知识是Js的线程问题。(有删减)
var cnt = 0;
for(var i = 0; i < 10; ++i) {
setTimeout(function() {
cnt += i;
}, 0);
}
setTimeout(function(){
console.log(cnt);
}, 0);

由于Js是单线程,遇到循环的时候setTimeout会在循环结束后才执行,所以cnt所加的i总是10,最后cnt的值是100。

var cnt = 0;
for(var i = 0; i < 10; ++i) {
    setTimeout(function() {
        cnt += i;
    }, 0);
}
console.log(cnt);

稍作改动,代码的运行结果是0,很容易理解。

二:向 setTimeout 传递字符串字面量

var s = 1;
function test()
{
    var s = 2;
    setTimeout('console.log(s)', 0);
}
function test2()
{
    var s = 2;
    setTimeout(function(){
        console.log(s);
    }, 0);
}
test();
test2();

传入字符串字面量时,字面量会在全局作用域内被解析,所以上面输出 1 和 2 ;

系统中接口调用的单例化(third_party中的一路财富接口)

场景介绍:
1、以前每次调用接口时,都需要new一个接口对象,再调用接口,这样不仅浪费资源,代码也比较繁琐;
2、理想的调用方法是:接口全静态。这样不仅运行效率高,而且调用方便;
思路整理:
1、首先静态方法无法调用非静态方法,如果把接口静态化,调用日志记录接口时会出现问题(CiLogs);
2、基于第一条,接口全静态的想法不可取。转而求其次,接口全部单例化,用静态类来调用;这样既可以记录日志,调用也非常方便。
改造:
1、首先看third_party代码结构(特指ylcf, xinyi类似):
com ----存放工具类,比如加密、字符串操作函数
entity ----未使用,可以删除
service ----存放了所有接口,按文件分类
config.php ----配置文件,主要是接口的转换
demo.php ----接口测试文件
ylcfClass.php ----引入service并实例化,对外提供统一的接口访问

PS:config.php担负接口管理的职责,但是在service中仍然出现了具体的接口,config没发挥好管理职责。
    更好的做法是,config.php中用键值对(可以是属性名-值,也可以是数组键名-值等等,属性名和键名都算“键”)保存接口,service中只出现config中提供的“键”,不接触接口。

2、把ylcfClass.php所有方法都静态化,用静态属性保存service的实例,保证每个service实例的单一性;

3、把com工具类静态化;

4、以前在 controller 或 model 里面调用接口是这样的:
        include_once("xxxx/ylcfClass.php");
        class A
        {
            ......
            $yc = new YlcfClass();
            $yc->xxx()->yyy();
            ......
        }
    为了美观,我们先创建一个 library: YlcfApiTool.php, 里面使用静态方法,调用 YlcfClass。
    然后在 controller 或 model 里这样调用:
        class A
        {
            public __construst()
            {
                $this->load->library('YlcfApiTool');
            }
            ......
            YlcfApiTool::xxx()->yyy();
            ......
        }
5、日志处理:
    整个过程没有修改日志处理流程,所以不受影响。

总结:
1、静态化可以提高代码效率,特别是在非脚本语言里;
2、用 library 包装了一遍接口,好处是可以用 load library 优雅地引入接口,调用也无需创建实例。

软件测试-大家都知道的

  1. 为什么要做测试(必答题)
    测试不能提高代码质量;
    自己写的代码自己一般检查不出来BUG;
    流程中复杂的数据结构,第三方模块和方法,内部细节不够详细(类似于黑盒),必须要测试才能发现潜在的bug;
    bug可能因为两块正常的代码耦合在一起而产生,所以通畅地测试整个流程很有必要;

  2. web开发的测试包括哪些
    宏观的测试内容包括:
    用户感受:页面样式正确、文案通顺、交互正常、流程逻辑合理;
    运营需求:流程(样式、文案、引导等等)和目的一致,数据能随时统计;
    开发人员:代码重用率、需求扩展是否容易、文档是否齐全;

  3. 测试需要遵循哪些规则

  4. 有哪些常规的测试方法
    代码评审
    黑盒测试

  5. 有哪些测试工具,自动化测试工具
    Chrome DevTools
    postman(模拟请求)
    fiddler(抓包)
    Selenium(模拟访问 & 结果存储分析)

  6. 如何编写测试文档(范例)
    用例:
    描述:
    前置条件:
    后置条件:
    预期结果:
    是否通过:
    附链接参考:http://www.ibm.com/support/knowledgecenter/zh/SSYMRC_4.0.0/com.ibm.rational.test.qm.doc/topics/t_testcase_template_ref.html

  7. 在以往项目开发过程中,测试的时候遇到的问题(必答题)
    调用api错误(调了别人的分支、调用了测试分支);
    数据解析错误(传参不一样数据结构不一样的接口);
    sql错误:表缺少字段,或者存储了一个null到非null字段;