Javascript对象、原型、继承什么的最傲娇了

最近辞职鸟,又可以开心地无打扰地学习了。O(∩_∩)O~
现实是残酷的,JS的对象和继承这部分看了好几个月了,还是没有进展,理解起来一团糟,超级抽象。
于是我开始自怨自艾,我开始后悔当初没有选择计算机专业,于是……

你是否和我一样的烦恼?
打住!
这不是你的错!

说说个人的感觉吧,W3School什么的讲得不错,不过太简略了,而网上百度出来的相关文章良莠不齐,好的文章多半是一些大牛的博客,需要一些背景知识才能看懂,而且他们好喜欢拿Java来类比,难不成为了这个比喻我还要去学习Java 囧rz……余下的,多数文章都是COPY,举的例子都那么雷同,正当我迷茫的时候,突然想起还有书这位好家伙。

书本是个好东西啊啊啊!!!
书的优点:知识系统!知识专业!表达严密!

下面进入正题

我觉得上面的吐槽才是主要的内容,下面的主要是笔记吧。

Javascript的对象这一节相信不是那么简单就能理解的,个人觉得主要的原因不是难,而是知识点比较广,另外比较抽象而又缺少实践。

先有几个概念要弄清:

  1. 先是对象的概念,这里不说了,为啥呢,因为网上的文章这部分都讲烂了。//感觉就是该详细的地方略过,不该详细的地方使劲举例。
  2. 然后是原型(prototype),网上说那么玄干嘛!说白了就是一个特殊的实例。对一个构造函数A来说,A的原型就是A的一个特殊的实例!用A.prototype来表示。这里补充一下,原型是对构造函数来说的,实例没有原型的说法,不过在浏览器内部,每一个实例会有一个_proto_的指针,指向的是把a生出来的时候A的原型,这个指向不可见,不过在chrome或者FF的控制台里面看得到,用a._proto_表示的,并且A的prototype和a的_proto_不是永远相等的!当a从A生出来之后,如果执行A.prototyoe=YYY,a._proto_就和A.prototype没有一毛钱关系了!A的原型就是YYY了,后来A生出来的宝宝的_proto_也是指向YYY了,而a._proto_还是指向最初的那个A的原型。

上面的prototype这个概念把我搞得好辛苦啊,还是看书好,对了,这本书就是《Javascript高级程序设计》Professional.JavaScript.for.Web.Developers.已经出到第三版了,我看的是第二版的。
然后就是几个检验实例和构造函数之间关系的函数。

  • constructor属性(Object.prototype.constructor):指向创造prototype属性所在的构造函数,是prototype的一个属性,可修改,除非是基本数据类型1、'1'、true。例:alert(a.constructor) //A
  • instanceof操作符:检查实例是否指向某个构造函数的原型。constructor属性指向是唯一的,instanceof就不是了,a可能既是A的实例又是B的实例,同时所有的实例都是Object的实例。例:
function A(){};
function B(){};
B.prototype = new A();
b = new B();
b instanceof B; //true
b instanceof B; //true
b instanceof Object; //true
  • isPrototypeOf()方法:检测某个原型是否是被某个实例所指向,正好和instanceof操作符逆向的操作,也就是说a instanceof A 等于 A.prototype.isPrototypeOf(a)。
    继续使用上面的例子,那么B.prototype.isPrototypeOf(b)就是true,A.prototype.isPrototypeOf(b)也是true。

注意这里的A.prototype和B.prototype是不等的,然后,B.protoype有一个隐藏的_proto_属性指向了A.prototype,而b的_proto_指向B.prototype,所以b当然也指向A.prototype啦。

  • hasOwnProperty()方法:如果实例的某个属性来自于实例,返回true,其余的比如来自于原型啊,就false。例:alert(a.hasOwnProperty("name"))
  • in操作符:不管实例的某个属性来自于实例自身还是实例通过原型访问到的,只要存在,都是true。例:alert("name" in a)。 PS:对于in操作符还有一个地方出现就是for in循环。

来,我们做点题目巩固一下。

function A(){};
function B(){};
function C(){};
B.prototype = new A();
b = new B();
B.prototype = new C();
b2 = new B();
b instanceof A; //true
b instanceof B; //false
b instanceof C; //false
b instanceof Object; //true 
b.constructor; //A
b2.constructor; //C

看完了还是一头雾水?那么……看书去吧o( ̄▽ ̄)o

轻松理解JS回调函数

前言:在前端有个现象,就是由于这是一个比较新的职业,加上很多的前端er都是学计算机出身的,所以对于一些概念,特别是js部分,喜欢用其它的语言,比如C啊、java啊来类比。
对于像我这种半路出身的人来说,这样反而更复杂了!关于js的回调函数,你百度一下,出来的结果相信很难看懂。其实本来就是很简单的东西。
一、什么是回调函数
就是函数a调用了函数b,通常是b是通过参数的形式被a调用:a(b)这样。
提问:为什么不直接function a(){ function b(){} }呢?因为这样b就限定只能叫b了。function a(anyNameYouLike){}这样在a内部调用使用anyNameYouLike()即可,而不用管传入的函数原本叫啥。

二、使用回调函数的情景
情景:需要a执行完了产生一个结果,再执行b进一步处理这个结果,也可能需要函数c来进一步处理这个结果。为了代码的重用和灵活性,直接写在a里面会很冗长。
三、回调函数的简单例子

function a(step2) {    
    var result='XXX';
    step2(result);
};

function b(data){ 
	//处理data
}; 

a(b); 

注意到上面的b传入a中不加括号。
四、本文结束,希望可以帮到你。
不过正如开头说到,我不是学计算机出来的,所以本文的某些概念可能不正确,如有误恳请指出。

jQuery中append特性分析(二)

昨天分析了B→A,B为多个,A为单个的情况,那么B所包含的元素将会脱离原来的位置,移动到这个唯一的A里面;但是如果A为多个呢?按照之前的说法,B将会自动clone到A中每一个元素中,事实如此吗?

先看下面的代码:

<style>
    table{margin: 10px;}
    .t1{border: 1px solid red;}
    .t2{border: 1px solid blue;}
    .t3{border: 1px solid green;}
    .t4{border: 1px solid yellow;}
</style>

<table class="t1">  <tr>    <td>table 1</td>    </tr> </table>
<table class="t2">  <tr>    <td>table 2</td>    </tr> </table>
<table class="t3"></table>
<table class="t4"></table>

<script>
    var tds=$('table.t1 tr');
    $('table').on('click','td',function(){
        $('table').append(tds);
    });
</script>

点击查看DEMO演示。

一共有4个表格,表格1和表格2有内容,表格3、4为空。对应前面,B是表格1的那一行内容,A将是所有的4个表格,按照开头的说法,点击一次单元格,B将被复制到所有的表格,但是点击之后的现象是:

第一次点击,表格2、3、4都增加了一行;
第二次点击,表格1、2、3都增加了一行;
第三次点击,表格1、2、3都增加了一行;
……

后面的每次点击,都只是表格1、2、3增加一行。

个人的猜测:

tds指向的表格1的那一行tr已经被移动到了表格4,这个通过firebug的输出功能也能看到,tds已经指向了表格4,正因为tds后面都是直接指向了表格4的仅有的那一行,所以以后的每次增加,表格4都不会变化,也部分验证了上一篇文章说到的,如果B来自于A,那么B→A将执行移动。表格4只有一行了,所以就抽出又插进去,没有变化;而其余的3个表格执行的是添加tds的clone。

所以上面的都好解释了,那么第一次执行了什么呢?

第一次,tds指向的是表格1的那一行tr,所以表格1没变化,其余各表格增加tds的clone,但是,关键是执行完第一次的点击活动之后,tds指向了表格4,这一点非常奇怪。

我又改了一点点代码,变成了

var tds=$('table tr')

点击查看DEMO演示。

这时候,前面3个表格都没有异常,按预想的增加了两个tr。。。不对,表格2的内容应该是table2,table1,但是现在和表格1和表格2一样,成了table1、table2的顺序。并且表格4的内容一直固定了,并且tds指向了表格4(还是通过firebug的console查看tds,鼠标移动到输出的tds,然后页面居然指示到了表格4!)。

总结:对于以上种种奇怪的现象,只有通过查看jQuery源代码才知道了。那个一对多就自动复制的说法不可靠啊,目前来看,只要A中的元素个数较多的时候,就有莫名其妙的问题,好像B最终都会指向A中的最后一个元素。暂时的解决办法是,开始指向的时候、后面添加的时候都使用clone方法好了。

可能本文说得有点不清楚,而且这样的情况也比较极端,然后本文没有完结,有一天明白了再来补充吧。

jQuery中append特性分析(一)

jQuery中append、insert、after等函数的特性刚接触会摸不着头脑,为什么有时候A.append(B)执行之后B会消失?有时候又不会?本文就是为此疑惑准备的,如果不想看过程,可以直接跳到文末的总结处。

方法 源包装集/字串 目标包装集体 特性描述
A.append(B)  
  B
 
  A
若目标包装集只匹配一个元素,则源(也包括同源包装集匹配的所有元素)将被移动到目标位置;若目标包装集包含多个元素,则源将保留在原来的位置,但同时复制一份相同的副本到目标位置。

由此,若目标只匹配一个元素时,使用前述方法后源将被删除。
B.appendTo(A)
A.prepend(B)
B.prependTo(A)
A.before(B)
B.insertBefore(A)
A.after(B)
B.insertAfter(A)

举例说明:在上图中,A.append(B)表示把B添加到与A匹配的所有元素的现有内容后面,因此B是源,A是目标包装集。

以上内容是我从网上搜索到的,现在具体说明。比如,有一个table,只有一行tr。
jAppend2
然后为td绑定事件,使得每一次点击td都会变红,语句:

var tds=$('table tr');
$('table').on('click','td',function(){
  $(this).css('background-color','red');
  $('table').append(tds);
});

jAppend4
点击有什么现象?单元格变红了,但是你可能很奇怪,为什么table没有append内容呢?使劲点,使劲点,还是只变红不增加。原来,tds只是指向了$('tr')这个jQuery对象,按照上面清单的解释,相当于是抽出第一行的tr,然后又把它自己插入table,所以,等于没有变化嘛,我们加上.clone()方法。

var tds=$('table tr').clone();

然后我们发现,成功了,点击一下,table内容扩大了一倍。但是,继续点击,不会增加了????
jAppend3
原理分析:此时虽然在给tds赋值时候加上了clone,但是tds依旧只是一个对象,它只能使用一次,所以,如果想要达到我们预想的目的,应该是

$('table').append(tds.clone());

最终的符合我们预期的代码如下:

var tds=$('table tr').clone();//此时tds复制了最原始的时候table的所有tr元素。
$('table').on('click','td',function(){
  $(this).css('background-color','red');//这句用来显示当前点击的位置
  $('table').append(tds.clone());
});

如果我们把第一行的clone去掉会怎样?代码变成:

var tds=$('table tr'); //此时tds指向此时,也就是最初的时候table的所有tr元素。
$('table').on('click','td',function(){
  $(this).css('background-color','red');
  $('table').append(tds.clone());
});

发现问题了?后面每次增加的那行都完整复制表格第一行的样子!如果第一行已经有格子被“污染变红”了,新增的那一行也是这样!图如下所示
jAppend
原因很简单,因为js的赋值其实是传地址而不是传值,看起来是在操作tds,其实是对tds指向的对象进行操作,所以tds其实是指向table的第一行,当然复制也是复制它咯。所以我们应该最开始就把它clone出来,避免后期被改变。

以上所有是对于B为jQuery对象的情况,如果是字符串呢?就像

tds=" 1 2 ";

这种情况下,tds能够随便用任意多次。

总结:对于上文的几种对DOM操作的方法,假如B是jQuery对象,把B应用到A上面,A也是一个jQuery对象,那么如果A的长度大于1,则B不会受影响,可以反复使用很多次;如果A长度为1,那么B应用到A之后,B会被移动(看起来就像从原来的位置删除了,但是没有被删除,只是被移动到了A的某个位置)。
如果B是一个简单的字符串,那么B就可以随便使用任意多次而不会被移动。

JS中定义函数的两种方式的区别

在JS中有两种定义函数的方式,

  1. function test(){...}
  2. var test=function(){...}

这两种方式定义的函数是有区别的。

区别一:调用和声明的顺序

  • function方式定义函数可以先调用,后声明。
  • var 方式定义的函数,不能先调用函数,后声明,只能先声明函数,然后调用。

请看代码:

//aaa();这样调用就会出错
var aaa = function(){
  alert("A");
}
aaa();//这样就不会出错
//先调用后声明,可使用function方式定义
bbb();
function bbb(){
  alert("B");
}

区别二:作用域的区别

说到函数,自然少不了作用域的问题。经过我自己的测试,发现两者的作用域是有很大的区别的,我想通过下面 四个例子进行分析

例子1,使用的是function的方式定义

a=1;
if (a==1){
  function tx(){alert('1')}
}else if(a==2){
  function tx(){alert('2')}
};
tx(); //'2'

想好了继续下面的第二个例子,使用var的方式定义

a=1;
if (a==1){
  var tx=function (){alert('1')}
}else if(a==2){
  var tx=function (){alert('2')}
};
tx(); //'1'

继续第三个例子

function outer(){
  alert('outer');
  function inner(){alert('inner'); };
};
outer(); //'outer'
inner(); //'inner未定义'

最后一个是结合了对象的例子:

var ob={
  xx1:function (){
    function gd(){ alert('gd'); }
  },
  xx2:function (){ gd(); }
  };

var ob2={
  xx1:function (){
    gd =function (){ alert('gd'); }
  },
  xx2:function (){ gd(); }
  };

//ob.xx2();  //'gd'未定义
//ob2.xx2(); //'gd'未定义
ob2.xx1(); //生成一个全局函数gd
ob2.xx2(); //alert('gd')

原理分析:

因为网上没有找到相关的资料,所以下面是个人的测试分析,如有错误请指出。

function方式定义的函数的作用域是当前的环境,这在第3个例子中最明显,所以内部如果用function方式定义,外面是访问不到的。

而使用var方式定义的函数,其实是先生成一个匿名函数,然后定义一个变量指向这个匿名函数,函数作用域决定于这个变量的作用域,比如不加var就是全局了,如果加上var那么就和function方式定义的函数作用域一样了。

这里要说一下例1和例2,例1中后面的函数把前面的函数定义冲掉了,同名函数定义了两次的感觉,js的语法就是这么规定的,后面定义的同名函数取代前面的;而例2中实际上生成了两个匿名函数,然后根据条件让tx指向其中某个函数。

顺便抱怨一个Wordpress自带的编辑器,太难用了,会“聪明”地把段前空格吃掉,导致代码缩进很难看。目前我是切换到源码状态进行编辑的,有更好的办法吗?