javascript的学习流程

任何一门语言的学习都可以从下面几个方面入手:

数据类型

操作符

表达式

语句

流程控制语句

函数

对象

内置数据类型的学习

内置函数的学习

内置对象的学习

数据库的学习

功能的实现

错误,调试和运行


javascript也不例外,我将根据自己总结的这个流程来学习任何一门语言。


javascript的数据类型


[if !supportLists]1. [endif]1.Number 类型

[if !supportLists]2. [endif]2.String 类型

[if !supportLists]3. [endif]3.Object 类型

[if !supportLists]4. [endif]4.Boolean 类型

[if !supportLists]5. [endif]5.Null 类型

[if !supportLists]6. [endif]6.Undefined 类型


Var

num = 100 ; tpye of 是number类型

Var

str = ‘闫岐’; type of是string 类型

Var un;   tpye of是undefined 类型

Var box = true; type of 是 boolean类型

Var base = {}; type of 是object 类型

Var box = null; type of 是object类型








NULL 和Undefined的区别


Undefined 是什么意思?当你


Var test;


声明了一个值的时候,却没有给它赋值,那这个就是undefined


null是什么?当你


Var test = null


为它赋了一个NULL值的时候,你是希望它以后是为了保存某个值的,只是暂时把它设置为“空”,这样,当你以后在用

If(test !== null){

//说明已经赋值了,可以进行操作了

}

检查的时候,就可以进行操作了。


这种情况应用于当你需要创建一个对象,却不知道这个对象应该初始化为那个值的时候使用


undefined: 代表一切未知的事物,啥都没有,无法想象,代码也就更无法去处理了。null: 有那么一个概念,但没有东西。无中似有,有中还无。虽难以想象,但已经可以用代码来处理了。


boolean类型

String   任何非空字符为真,空字符为假

Number 任何不等于0的为真,为0和为NaN的为假

Object 任何对象都为真,NULL的对象为假

Undefined 为false

Boolean () 用来将任何类型的值转化为boolean类型


空的字符串,数值0,null,undefined 都是假,其它所有都为真




Number类型

NaN是一个非数值的值,用来表示一个本来要返回数值的操作数未返回数值的情况


Var box = 0/0 NaN

Var box = 12/0

Var box =12/0*0 NaN


任何与NAN相运算的结果都是NAN,NAN不等于NAN。



Number() 可以将任何数据类型转化为整数。


记住一点,null和undefined都不等于0,他们不是一个概念,但是它会默认转为0和NaN,也就是不能进行比较运算









专门的字符串转换整数函数:


parseInt()整数类型的转换;


Alert(parseInt(‘456Lee’)); //返回456整数部分


Alert(parseInt(‘Lee456Lee’));  //NaN,如果第一个不是数值,只能返回NAN


Alert(parseInt(‘12Lee456’)); // 12,从第一个数值开始,到最后一个连续数值结束


Alert(parseInt(‘56.12)); // 56,小数点不是整数,会被去掉


Alert(parseInt(‘*’));     //不是数字,返回NAN


//它提供了第二个参数来转换进制




parseFloat()是用来浮点数的转换的


Alert(parseFlaot(“  123LEE  ”));  123,去掉LEE的部分


Alert(parseFlaot(“  oxA  ”));  不认识16进制


Alert(parseFlaot(“  123.4.5  ”)); 只认一个小数点


Alert(parseFlaot(“0123.400   ”));  123.4去掉前后导


Alert(parseFlaot(“  1.234e7  ”));  12340000转化为普通数值



String 类型

Var box = ‘yanqi’;

Var box = “yanqi”;


单双引号在JS里头没有任何区别,但必须成对使用,不可以穿插使用。


同样,string()方法用来将任何类型的值转化为字符串



toString 方法用来将数值转化为字符串


Var box = 10;

box.toString(8); //八进制

box.toString(10); //十进制

box.toString(16);  //十六进制



记住,最常用的转化主要集中在数字和字符串的转化,数字转化为字符串tostring(),字符串转化为数字parseInt()



Object 类型

Var box = new Object(); //通过new来创建对象










----------------------------------------------------------数据类型结束-----------------------------------------------







运算符

算数运算符


加减乘除模(取余)


如果在算数运算的值不是数值,那么JS将会使用number()转型函数将其转换为数值,属于隐式转换


基本所有的隐式类型转化都发生在运算符中





关系运算符


小于<   大于> 等于==  小于等于<= 大于等于>= 不等于!=全等于=== 全部等于!=== (除了值相等,数据的类型也必须相等)


规则:

[if !supportLists]1.   [endif]1.两个操作数都是数值,则数值比较  3>2

[if !supportLists]2.   [endif]2.两个操作数,都是字符串,则比较两个字符串对应的ASC编码值  ‘2’> ‘4’

[if !supportLists]3.   [endif]3.两个操作数,有一个是数值,则将另外一个转换为数值,再进行数值比较  ‘2’> 4

[if !supportLists]4.   [endif]4.两个操作数,有一个是对象,则先调用valueof()或者toString()方法,在用结果比较  1 <对象



特殊类型的比较:


boolean的true转化为1,false转化为0


字符串+数字 = 字符串


如果跟NaN进行运算,始终结果都是NaN,等于或者不等于返回false,true .并且NaN和自身不等

全等和全不等的判断上,值和类型都相等,才会返回true,否则返回flase


如果两个都是对象,则比较它们是否是同一对象,如果指向同一对象,则返回true,否则返回false


Undefined == 0 //结果不成立,false


Null  == 0 //结果不成立,false


null会自动转换为0,undefined 自动转化为NaN,但在比较运算中,null和undefined没有自动转换。





逻辑运算符


[if !supportLists]1.   [endif]1.逻辑与&&


规则:

①第一个操作数是对象,则返回第二个操作数  var box=对象&&(5>4),true,返回第二个操作数

②第二个操作数是对象,则第一个操作数返回true,才返回第二个操作数,否则返回false


举例:


(5

> 3)&& 对象,因为第一个操作数成立,则返回对象

(5

> 6)&& 对象,因为第一个操作数不成立,则返回false



③有一个操作数是null, 则返回null

④有一个操作数是undefined则返回undefined

⑤它属于短路操作符,如果第一个操作符是false,则不管第二个操作符,始终返回false.



这个情况比较复杂,看实际情况再来看



[if !supportLists]1.   [endif]2.逻辑或||


规则:

①第一个是对象,则返回第一个var box = 对象 || (5>3) ,返回对象,无论第二个怎样

②第一个操作数求值结果为false

,则返回第二个操作数  ,否则返回true . Var box = (5>3) || 对象,返回true

③两个操作数都是对象,则返回第一个操作数 var box = 对象1 || 对象2,返回对象1,如果不存在就返回对象2.

④var box = null|| null  // null

⑤var box = NaN|| NaN //NaN

⑥var box =undefined || undefined //undefined

⑦同样是短路操作符,当第一操作数的求值结果为true ,就不会对第二操作数求值了




[if !supportLists]1.   [endif]3.逻辑非


先将这个值转换为布尔型,然后取反


Var box = !(5>4)         false

Var box = !{}        false

Var box =!’’         ture

Var box =!’lee’          false

Var box =!0          true

Var box = !8         false

Var box = !null          true

Var box = !NaN           true

Var box = !undefined       ture //这三个本身就是false,如果转换为布尔型,相反就是true






-------------------------------------运算符结束---------------------------------------------------------------------



对象和数组

数组表示有序数据的集合,而对象表示无序数据的集合。如果数据的顺序很重要,就用数组,否则就用对象。


JS中的数组无法使用除了数字作为下标的关联数组的方式,PHP则可以创建关联数组,在JS中,关联数组相当于JS的对象

如何创建对象

[if !supportLists]1.   [endif]1.使用NEW运算符



Var box = new Object(); 

Box.age = 25;

Box name = “闫岐”; //创建对象的属性


[if !supportLists]1.   [endif]2.NEW关键字可以省略


Var box = Object ();


[if !supportLists]1.   [endif]3.使用字面量的方式


Var box = {

Name : ‘闫岐’,

Age : 25

};


[if !supportLists]1.   [endif]4.属性字段也可以用字符串形式


Var box = {

‘name’:’闫岐’,

‘age’:25



};


[if !supportLists]1.   [endif]5.使用字面量及传统复制方式


Var box = {};

Box.name = ‘闫岐’;

Box.age = 25;


属性除了.输出之外,也可以用数组形式[]输出,比如box.name 或者box[‘name’]

是不是类似于PHP中的关联数组的方式?就是如此/



[if !supportLists]1.   [endif]6.给对象创建方法


Var box = {

Run : function(){

//我的方法

}

}


[if !supportLists]1.   [endif]7.删除对象的属性


Delete box.name; //删除了name 属性





数组

[if !supportLists]1.   [endif]1.NEW运算符创建数组



Var box = new Array ();

Var box = new Array(10);

Var box = new Array(‘闫岐’,29,’程序员’)


[if !supportLists]1.   [endif]2.以上的三种方法可以省略new关键字



Var box = Array ();

Var box = Array(10);

Var box = Array(‘闫岐’,29,’程序员’)


[if !supportLists]1.   [endif]3.使用字面量的方式创建数组



Var box = [];

Var box = [‘闫岐’,25,’程序员’];


[if !supportLists]1.   [endif]4.数组是使用下标[]来读取每个元素的


Box[1] = ‘’;

Box[2] = ‘’;



[if !supportLists]1.   [endif]5.可以使用length来获取数组的长度


Box.length


[if !supportLists]1.   [endif]6.创建一个复杂的数组


Var box = {

{

Name:’闫岐’,

Age:25,

},

[‘马云’,29,‘淘宝’],

‘计算机编程’,

25+25,

New Array(1,2,3)

}


证明了数组的每个元素可以是任何类型的元素


感觉JSON和JS的不同在于写法确实不同,JSON必须用双引号,JSON只是数据格式





数组的方法

toString();把数组转换为字符串,并返回结果

valueOf();

toLocaleString();

Join();把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。

Push()和pop() 用栈的方式来操作数组,后进先出

Push()和shift()列队的方式来操作数组,跟栈是相反的,先进先出

Unshift()用来在前端添加一个元素,shift用于在前端删除一个元素

Reverse()逆向排序

Sort()正向排序

Concat()方法可以给予当前数组创建一个新数组

Slice()方法可以给予当前数组获取指定区域元素兵创建一个新数组

Splice()主要用途是向数组的中部插入元素




----------------------------------------------------------------对象结束----------------------------------------------


日期和时间




Var box = new Date();  //创建一个日期时间 对象,构造方法可以传参数,来指定时间


//返回的结果根据浏览器的不同而不同,一般不会直接使用


提供了两个方法:


Date.parse()

//必须接受一个表示时间的字符串参数,然后根据这个时间返回相应的毫秒数


格式:


‘月/日/年’6/13/2011

‘英文月名 日,年’May 25,2004

‘英文星期几 英文月名  日 年 时:分:秒 时区’Tue May 25 200400:00:00 GMT-070

Alert(date.parse(‘6/13/2011’)) //1307894400000


如果Date.parse()没有传入或者不是标准的日期格式,将会返回NaN


Alert(Date.parse()); //NaN


如果想输出指定的日期,可以把Date.parse()传入到Date的构造方法中


Var box = new Date(Date.parse(‘6/13/2011’)); //Mon Jun 13 2011 00:00:00 GMT+0800


Var box = new Date(‘6/13/2011’); //默认自动后台调用Date.parse();





PS:如果日期写错了,浏览器会+1或者-1这种形式来保证日期格式的正确,具体怎么做,还是看浏览器的


Date.UTC()


//同样的返回毫秒数


格式:


年份,基于0的月份,月份中的那一天,小时数,分钟,秒以及毫秒


Alert(Date.UTC(2011,11));//13226976000000  只有前两个是必须的


如果Date.UTC()参数错误,同样会返回负值或者NaN


Alert(Date.UTC()); //负值或者NaN


如果要输出指定的日期,就直接放入Date构造方法中去:


Var box = newDate(Date.UTC(2011,11,5,15,13,16));

省略Date.UTC的话,返回的是本地时间,否则返回的是东一区的时间


总结:


Var box = new Date(写入parse的格式|写入UTC的格式),返回自己的时间结果。

得到日期后呢,我们有一些专门用于将日期格式化为字符串的方法


Var box = new Date(‘parse格式’|UTC格式);//已经得到了我们的结果


box.toDateString()  以特定的格式显示星期几、月、日、年

box.toTimeString() 以特定的格式显示时分秒和时区

box.toLocalDateString() 以特定的地区格式显示星期几、月、日、年

box.toLocalTimeString() 以特定的地区格式显示时分秒和时区

box.toUTCString()  以特定的格式显示完整的UTC日期



组件方法

UTC时间,国际统一标准时间 = 当前的时间 +8个小时


box.getTime()   //获取日期的毫秒数  //获取当前时间(从1970.1.1开始的毫秒数)

box.setTime() //以毫秒数设置日期

box.getFullYear() 获取四位年份 //获取完整的年份(4位,1970-????)  

box.setFullYear() 设置四位年份 

box.getMonth()       获取月份  //获取当前月份(0-11,0代表1月)

box.setMonth()       设置月份

box.getDate()     获取日期 //获取当前日(1-31)

box.setDate()     设置日期

box.getDay()      返回星期几 //获取当前星期X(0-6,0代表星期天)

box.setDay()      设置星期几

box.getHours()       返回时  //获取当前小时数(0-23)  

box.setHours()       设置时

box.getMinutes()  返回分钟 //获取当前分钟数(0-59)

box.setMinutes()     设置分钟

box.getSeconds()  返回秒数 //获取当前秒数(0-59) 

box.setSeconds()  设置秒数

box.getMilliseconds()    返回毫秒数 //获取当前毫秒数(0-999)

box.setMilliseconds()    设置毫秒数

box.getTimezoneOffset()  返回本地时间和UTC时间相差的分钟数


以上方法,除了getTimezoneOffset之外,都有UTC方法,例如setDate 就会有setUTCDate方法






正则表达式





1.new创建


Var box = new RegExp(‘box’,’ig’);


i 忽略大小写

g 全局匹配

m 多行匹配   记住它只对^和$模式起作用


[if !supportLists]2.   [endif]2.字面量的方式创建


Var box = /box/ig;




[if !supportLists]2.   [endif]3.方法


Test  正则.字符串, 返回true或者false ,看看字符串是否符合正则


Exec 正则.字符串,返回符合匹配的字符串,以()为单位,0返回完整的,从1-N返回括号内的元素——也就是返回字符串的子字符串。


var  pattern = /Box/ig;var  str = 'This is a

Box!That is a Box ';var box = pattern.exec(str);alert(box[0]);


你可以看到,它box[0]应该是它的完整匹配,如果有括号,那么[1]应该返回它第一个括号内的内容。


你要知道,全局的概念是什么?尽可能多的匹配,如果你给exec 加入了/g,它仍然是返回第一个匹配的结果,但是,你再执行以下,就可以获取第二个匹配结果了。这跟它的下标【】返回子元素并不冲突,记得,它的下标永远返回的是字符串中的子匹配。


var regx=/user\d/;

var str=“user18dsdfuser2dsfsd”;

var

rs=regx.exec(str);//此时rs的值为{user1}

var

rs2=regx.exec(str);//此时rs的值依然为{user1}

如果regx=/user\d/g;则rs的值为{user1},rs2的值为{user2}



[if !supportLists]2.   [endif]4.字符串的正则方法


Match(正则)



Var pattern = /Box/ig;

Var str = ‘This is a Box!That is a Box’;

Alert(str.match(pattern));

//获取匹配的数组,如果不开全局,只匹配第一个Box

//注意它跟exec之间的区别,exec即便加了全局,也必须通过再次调用,match一次返回所有的结果。


如果 regexp 没有标志 g,那么 match() 方法就只能在stringObject 中执行一次匹配。如果没有找到任何匹配的文本, match() 将返回 null。否则,它将返回一个数组,其中存放了与它找到的匹配文本有关的信息。该数组的第0 个元素存放的是匹配文本,而其余的元素存放的是与正则表达式的子表达式匹配的文本。除了这些常规的数组元素之外,返回的数组还含有两个对象属性。index 属性声明的是匹配文本的起始字符在 stringObject 中的位置,input 属性声明的是对 stringObject 的引用。

如果 regexp 具有标志 g,则 match() 方法将执行全局检索,找到 stringObject 中的所有匹配子字符串。若没有找到任何匹配的子串,则返回null。如果找到了一个或多个匹配子串,则返回一个数组。不过全局匹配返回的数组的内容与前者大不相同,它的数组元素中存放的是 stringObject 中所有的匹配子串,而且也没有 index 属性或 input 属性。

注意:在全局检索模式下,match()即不提供与子表达式匹配的文本的信息,也不声明每个匹配子串的位置。如果您需要这些全局检索的信息,可以使用RegExp.exec()。


开全局和不开全局差别很大,开了全局只能获得所有的结果,不开全局,能得到子表达式的信息


//个人感觉是统计字符串中某个子串出现的次数




Replace(正则,替换的内容)


Var pattern = /Box/i;

Var str = ‘This is a Box!That is a Box’;

Alert(str.replace(pattern,’Tom’));


//如果不加全局,只替换第一个,加了全局,所有匹配都会被替换


//将字符串某些字符进行替换




Search(正则)  比较简单,正确的话返回它的位置,不正确的话返回-1,只要找到一个就返回了不需要G,全局匹配。


//查找字符串中的某个元素的位置




Split(正则)



Var pattern = /!/i;

Var str = ‘This is a Box!That is a Box’;

Alert(str.split(pattern));



//根据某个字符来拆分,将字符串拆分成数组。


重复的字符串\1引用下即可,例如aa则\w\1中\1匹配到的就是\w匹配的那个字符的重复项





函数


[if !supportLists]1.   [endif]1.function box (num1,num2){

Return num1 + num2

}

//普通函数声明


[if !supportLists]1.   [endif]2.var box = function(num1,num2){

Return num1 + num2


}


//变量初始化的方法


[if !supportLists]1.   [endif]3.函数因为是对象,所以,它可以像传值一样传递到另外一个函数内


Function box (sum,num){

Return sum+num;

}


Function sum(num){

Return num+10;

}

Var result = box(sum(10),10);

//结果为30


这一切之所以这么复杂,或者看起来这么变化莫测,本身就是因为函数也可以作为参数被传递,我们是知道的,在C,C++中,参数可以是简单的数据类型,整数,浮点,字符串,也可以是类(类传递的是一个指针),但我是没见过能传递函数这种参数的。

但在JS中,函数可以拥有方法和属性,因为本身它就是个对象,函数可以作为参数传递,同样是因为它是个对象。




--------------------------------------------------------------函数结束------------------------------------------------



突然明白了正则表达式的惰性模式是个什么意思了? 符号?:是惰性匹配

一般的? * +都是零到一,零到多,一到多,一旦开启了惰性模式,那么,零到一的情况下,尽量匹配零,零到多的情况,尽量匹配零,一到多的情况,尽量匹配一。只要能满足条件就可以停止了。


上头是针对匹配次数的



.

变量以及作用域


之前的变量具备块状作用域,在(){}里头的变量只存在(){}里头,超过了它的作用域,该变量不可见。


JS的变量分为基本类型和引用类型,它们分别放入栈和堆之中,基本类型的在栈中保存实际的数值,引用类型在堆中,该变量实际保存的真是一个指针。(因为它会变大变小)


按我的理解


var num = 10 放在栈中var box = new object() 放在堆中






将一个值赋给变量的时候,解析器必须确定这个值是基本类型,还是引用类型


基本类型:undefinednull boolean number string


///他们的值保存在栈空间,固定大小,不会变大变小


引用类型: 对象object


//他们的值放在堆中



对象保存的是一个地址,先从栈中读取地址,然后从地址找到堆中的值


堆和栈的区别可以用如下的比喻来看出:使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。//总结:栈由系统自动分类配,而堆由程序员来创建




动态属性

基本的类型是无法添加属性的,var

box =’string’; box.age=25;是不可以的



复制变量值


基本类型复制的是值本身,而引用类型复制的是地址


可能它本身储存的就是个地址,我们是从地址中找到值的。


所以,针对对象的复制,可能只是复制一个地址。



Var box =’lee’


Var box2 =box ;




相互独立,互不影响


Var box = new object();

Box.name=’lee’;

Var box2 = box;



引用类型


如果你改变任何一个BOX的name值,它都会变化。




在引用类型中,BOX2其实就是BOX ,因为他们指向同一个对象



这里我们会考虑如何才能完成复制功能呢?

我自己感觉,构造函数已经帮我们完成了复制,批量生产对象的一个功能!




传递参数


分为传递基本型,传递引用类型


参数都是按照值才传递的



这里,我应该很清楚了,函数传参都是复制,默认情况下,它不会修改原本数据的值。

//函数传递参数,都是默认进行复制的,它不会修改原本数据的值var a = 10;var b = 20;function box(num1,num2){   return {       a: a + 10,        b: b + 10,   };}console.log(box(10,20));console.log(a);console.log(b);



JS没有按引用传递,PHP却可以,C++也可以。


//按引用来看看

Fucntion box (obj){


Obj.name=’lee’;


}

Var obj = new object();

Obj.name = ‘kkk’;


Box(obj);


Arlert(obj.name);


//还是lee,它不是按引用传递,只是传递引用类型,跟传递基本类型一样是复制,只不过它传递的是一个地址,基本类型传递的是值,所以,基本类型的操作不会修改本身,而引用类型的操作就会修改本身了。




检测类型


Var box = [1,2,3]; //数组


Var box2 ={};  //对象


Var box3 = /gg/;  //正则


上述都是对象,那我们可能想要知道他们是什么类型的对象?


Box instanceof Array


Box instanceof Object


Box instanceof RegExp


Box instanceof Fucntion


Box instanceof 某个构造函数



不能用instanceof来检查基本类型





作用域

所有不在函数内的变量和函数都是window对象下的,window是最大的,也就是全局作用域


JS仅仅具备函数内和函数外两部分的作用域




Var box = ‘blue’;

Function getBox(){

Var box = ‘red’;

    Returnbox;


}

Alert(getBox());




基本的包装类型


为了便于操作基本类型,JS提供了三个特殊的引用类型:


[if !supportLists]1.   [endif]1.Boolean

[if !supportLists]2.   [endif]2.Number

[if !supportLists]3.   [endif]3.String


作为基本类型,这三个类型可以调用系统本身自带的属性和方法,而如果你想要为他们单独创建一个属性和方法的时候,是不起作用的。


Var box = ‘Mr.lee’;

Box.name =’lee’;

Box.age = function(){

Renturn 100;


}

Alert(box.name);

Alert(box.age());




那么,你应该怎么办呢?


Var box = new String(‘Mr.lee’);

Box.name =’lee’;

Box.age = function(){

Renturn 100;


}

Alert(box.name);

Alert(box.age());


这样操作,确实有点混淆不清,尽量不这么做。




Number内置的方法

toString()  转换为字符串

toLocaleString() 根据本地数字格式转换为字符串

toFixed()  将数字保留小数点后指定位数并转化为字符串

toExponential()   将数字以指数形式表示,保留小数点后指定位数并转化为字符串

toPrecision() 指数形式或点形式表述数,保留小数点后面指定位数并转化为字符串





String 内置的方法

charAt(位置)  返回指定位置的字符


charCodeAt(位置) 返回指定位置的字符,以Unicode编码的形式


Concat(str1...str2)  将字符串参数串联到调用该方法的字符串中


Slice(n,m) 返回字符串N到M之间位置的字符串


Substring(n,m) 同上


Substr(n.m) 返回字符串N开始的M个字符串


//如果只有一个参数,slice ,substring,substr  功能相同



//他们负数的情况不同


Box.slice(-2)  字符串的总长度-2 ,从这个位置开始,到最后


Box.substring(-2)  返回全部字符串


Box.substr(-2)  字符串的总长度-2 ,从这个位置开始,到最后



Box.slice(2,-1)  从2开始到 字符串的总长度-1


Box.slice(-2,-1)  字符串长度-2 到字符串长度-1


Box.substr(2,-1) 第二个参数为负,直接转0





Indexof(str,n) 从N开始搜索的第一个str,并将搜索的索引值返回

lastIndexOf(str,n) 从N开始搜索的最后一个str,并将搜索的索引值返回 ,从N开始向前搜索



toLowerCase() 全部转化为小写

ToUpperCase() 全部转化为大写


Splice(位置,删除多少,要替换的值) ,返回被删除的元素数组


Splice(2,0,’yanqi’)在数组2下标的位置,插入yanqi这个值

Splice(2,1,’yanqi’)在数组2下标的位置,删除这个值,然后用yanqi来替代

Splice(2,3,’yanqi’)在数组2下标的位置,开始删除三个值,然后用yanqi来填补








-----------------------------------------------------------------包装类型结束----------------------------------------



面对对象与原型


工厂模式

我们都知道引用类型赋值不会产生一个相同的数据,将一个对象赋值给另外一个对象的时候,这两个对象实际指向的是相同的对象地址。


Var box  = {};

Box.name =’lee’;

Box.age = 25;

Var box2 = box;

//如果你去修改了box2当中的数据,box也会相应改变

Box.name=’jack’;

Box.age = 100;

Alert(box.name);

Alert(box.age);



为了解决这个问题,我们提供了工厂模式


Function createObject(name,age){

Var obj = new Object();

Obj.name = name;

Obj.age = age;

Obj.run = function (){

Return this.name +this.age +’运行中...’;

}

Return obj;


}

Var box1 = createObject(‘lee’,100);

Var box2 = createObject(‘jack’,200);

Alert(box1.run());

Aleft(box2.run());










构造模式

//这个是个构造函数


Function box(name,age){

This.name = name;

This.age = age;

This.run = fucntion(){

Return this.name + this.age + ‘运行中’;

}

}


Var box1 = new box(‘lee’,100); //new 构造函数

Var box2 = new box(‘jack’,200);


Alert(box1.run());

Alert(box2.run());


感觉构造函数跟普通函数很类似,有两点区别:


[if !supportLists]1.   [endif]1.属性和方法用this 关键字

[if !supportLists]2.   [endif]2.使用new来实例化




C++中,属性都是各自的,方法可能是公用的,但在JS里头,恐怕属性和方法都是私人的,另外对于this,在C++中,this代表的是对象的地址,它是默认被传递到对象方法中去的,因为方法是公共的,属性是私有的,我必须知道这个方法要处理的数据是哪个?是A,是B,还是C,所以,我会把对象A,对象B,对象C的地址传递到对象的方法中去。


实际上,C++中,对象的大小就是属性的大小,方法是另外存放的。


但在JS当中,你知道了,它的属性和方法都是单独的,也就是说它不需要传递指针给方法






它是怎么做的呢?


这是工厂模式和构造模式想结合理解的一个实例


Function box(name,age){

Var this = {}; //创建一个新的对象,叫this

This.name = name;

This.age = age;

This.run = fucntion(){

Return this.name + this.age + ‘运行中’;

}

//给this 赋值

Return this;

//返回这个this对象

}



创建和返回都是默认进行的,那么在JS中,this就是个新的对象,它就是简化了工厂模式,原理都是一样的。



记住,它们是模仿定义类 —— 实例化类,这种模式的。



实际上,他们是 定义一个构造函数 —— new 这个函数





原型

前面说到过,在JS里头,它的属性和方法都是单独拥有的,那么,它就有一个非常明显的缺点,那就是它无法共享属性和方法。




比如,在DOG对象的构造函数中,设置一个实例对象的共有属性species。

functionDOG(name){

this.name =name;

this.species

= ‘犬科’;

}

然后,生成两个实例对象:

var dogA =

new DOG(‘大毛’);

var dogB =

new DOG(‘二毛’);

这两个对象的species属性是独立的,修改其中一个,不会影响到另一个。

dogA.species

= ‘猫科’;

alert(dogB.species);

// 显示”犬科”,不受dogA的影响

每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。



考虑到这一点,Brendan Eich决定为构造函数设置一个prototype属性。

这个属性包含一个对象(以下简称”prototype对象”),所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。

实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。

还是以DOG构造函数为例,现在用prototype属性进行改写:

functionDOG(name){

this.name =name;

}

DOG.prototype

= { species : ‘犬科’ };var dogA = new DOG(‘大毛’);

var dogB =

new DOG(‘二毛’);alert(dogA.species); // 犬科

alert(dogB.species);

// 犬科

现在,species属性放在prototype对象里,是两个实例对象共享的。只要修改了prototype对象,就会同时影响到两个实例对象。

DOG.prototype.species

= ‘猫科’;  alert(dogA.species); // 猫科

alert(dogB.species);

// 猫科


五、总结

由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像”继承”了prototype对象一样。









实际我是这么理解的,_proto_指向了原型对象,而原型对象中的constructor指向了构造函数,两个家伙联系在了 一起. 构造函数找原型,原型找构造函数


实际的访问测试:


Box1.prototype

Box2.prototype

//访问不到

Box1._proto_

Box2._proto_

//IE下不行


Box1.constructor

Box2.constructor

//也是什么也访问不到


Box.prototype 却可以访问的到,因为你访问的是构造函数的原型属性,同时你也可以修改它


//box.prototype.constructor =box

//这里,一定要看清楚了,原型中的constructor指向的就是构造函数,谁的原型指向谁,毕竟原型是一个共享的对象,单独放置,一个地址共享。

//box1.constructor == boxbox2.constructor == box

//box1 instanceof box box2

instanceof box 也是可以的。

//box1 instanceof object box2

instanceof object 同样是对的


你是直接访问的,而不是通过_proto_或者constructor来带参数访问的,你要明白,只不过是通过这两个属性,我们获得了访问的权限,一定要明白这一点。




原型模式的执行流程:


[if !supportLists]1.  [endif]1.先查找构造函数实例的属性方法,如果有立即返回

[if !supportLists]2.  [endif]2.如果构造函数实例没有,则去它的原型中找,如果有,就返回


原型的修改方式:


[if !supportLists]1.  [endif]1.构造函数.prototype来修改属性和方法

[if !supportLists]2.  [endif]2.实例对象.属性或者方法来修改  (这个应该是错误的)






构造函数原型 isPrototypeOf()

这个方法用来判断,某个构造函数原型和某个实例之间的关系。

感觉就是判断它们是否共享了同一原型

alert(Cat.prototype.isPrototypeOf(cat1));

//true

alert(Cat.prototype.isPrototypeOf(cat2));

//true

实例对象hasOwnProperty()

每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。

alert(cat1.hasOwnProperty("name"));

// true

alert(cat1.hasOwnProperty("type"));// false


Object.getPrototypeOf()可以获取指定对象的原型


实例对象in运算符

in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。

alert("name"

in cat1); // true

alert("type"

in cat1); // true

in运算符还可以用来遍历某个对象的所有属性。

for(var prop in cat1) {

alert("cat1["+prop+"]="+cat1[prop]);


原型的写法——字面量的形式

//字面量的形式

Function box(){};

//创建了一个新的对象赋值给了box.prototype

Box.prototype = {

Constructor :box //强制

Name:’lee’,

Age:100

Run:function(){

Return this.name+this.age+’运行中’;


}



}


//普通的构造方式

Function box(){};

Box.prototype.name=lee;

Box.prototype.age=100;

Box.prototype.run=function(){

Return this.name+tis.age+’运行中’;

}


两种有什么区别呢?


Var box1 = new box();


当你实例化的时候,普通的构造方式,box1.constructor == box


字面量的方式,box1.constructor== object


所以,尽量不要用字面量的形式来创建,也可以强制加上一句话


Constructor :box




原型的缺点

[if !supportLists]1.   [endif]1.原型中的属性(也就是数据)不能传参进行初始化,是在写原型数据的时候就已经确定的值

[if !supportLists]2.   [endif]2.如果你修改了原型中的属性和方法,那么,第二个实例化对象的时候,属性和方法是修改后的,也就是它们共享这个值的操作结果。


如何正确书写原型


[if !supportLists]1.   [endif]1.把不需要改变的属性和方法放入原型中,其它的放入实例中去


Function 构造函数(){

This.name = ‘jack’;

This.age = 100;


}

构造函数.prototype={

Constructor:box

Run:function(){

//代码

}



}

//这种方法叫做构造+原型模式


你可以学习C++,将属性放入实例中,将方法放入原型中,这样就越来越像C++了


多次实例化只有一个地址



[if !supportLists]1.   [endif]2.减少原型的初始化次数,并把这一切封装到一起



Function 构造函数(name,age){

This.name = name;

This.age = age;


If(typeof this.run != function){

构造函数.prototype.run = function(){}

}


}

//多次实例化,只执行一次。



继承

子构造函数.prototype = new 父构造函数()不能直接使用

比如,现在有一个"动物"对象的构造函数,

functionAnimal(){

this.species = "动物";

}

还有一个"猫"对象的构造函数,

functionCat(name,color){

this.name = name;

this.color = color;

}


怎样才能使"猫"继承"动物"呢?

更常见的做法,则是使用prototype属性。

如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。

Cat.prototype = new

Animal();

Cat.prototype.constructor

= Cat;

var cat1 = new

Cat("大毛","黄色");

alert(cat1.species); // 动物

代码的第一行,我们将Cat的prototype对象指向一个Animal的实例。

Cat.prototype = new

Animal();

它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。但是,第二行又是什么意思呢?

Cat.prototype.constructor

= Cat;

原来,任何一个prototype对象都有一个constructor属性,指向它的构造函数。也就是说,Cat.prototype 这个对象的constructor属性,是指向Cat的。

我们在前一步已经删除了这个prototype对象原来的值,所以新的prototype对象没有constructor属性,所以我们必须手动加上去,否则后面的"继承链"会出问题。这就是第二行的意思。

总之,这是很重要的一点,编程时务必要遵守。下文都遵循这一点,即如果替换了prototype对象,

o.prototype = {};

那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。


这种方法只是将基类构造数据 + 原型数据  扔到 派生类的原型中去了,首先,数据不应该在原型中

//比如这里有两个构造函数,第二个构造函数要继承第一个构造函数的内容function animal(){   this.species = '动物';}animal.prototype.run = function(){   alert('所有的动物都会跑');}function cat(name,color){   this.name = name;   this.color = color;}//怎么让猫继承动物呢?//引导学生使用prototype属性//将一个基类的实例扔进派生类的原型中去cat.prototype = new animal();cat.prototype.constructor= cat;var cat1 = new cat('大毛','黄色');console.dir(cat1);//对于这个方法,首先,先在cat1自身属性当中去寻找,如果找不到,进去原型链去找,还找不到就去animal中原型链找,最后找到Object对象//cat1.run();//缺点:基类的构造数据和原型数据都放在了派生类的原型链中,基类的构造数据成了公共的了,修改一个等于修改全部.





基类.call(派生类,参数)不能直接使用

///比如这里有两个构造函数,第二个构造函数要继承第一个构造函数的内容function animal(){   this.species = '动物';}animal.prototype.run = function(){   alert('所有的动物都会跑');}function cat(name,color){   //通过call改变了animal中的this指向,变成了cat    animal.call(this);   this.name = name;   this.color = color;}var cat1 = new cat('大毛','黄色');//首先先在自身找,自身找不到,放到原型链中去找,最终找到Object对象console.dir(cat1);//alert(cat1.species);//原型链当中的方法并没有被继承过来//alert(cat1.run());




在派生类中,基类.call(this),就相当于在派生类中添加了this.name=name,this.age=age

它所有的数据和方法都被复制了一遍,只是this的指向是派生类.


但是,基类原型链里面的数据还是没有被继承过来




子构造函数.prototype

= new 父构造函数()

基类.call(派生类,参数) 一起用(不能直接用)


//比如这里有两个构造函数,第二个构造函数要继承第一个构造函数的内容function animal(){   this.species = '动物';}animal.prototype.run = function(){   alert('所有的动物都会跑');}function cat(name,color){   //通过call改变了animal中的this指向,变成了cat    animal.call(this);   this.name = name;   this.color = color;}//基类的原型数据还是没有进去cat.prototype = new animal();cat.prototype.constructor= cat;var cat1 = new cat('大毛','黄色');console.log(cat1);


这种方法的话,原型数据依然是没有进来,并且,派生类的构造函数和原型中都会有基类构造函数里面的数据,造成了重复和浪费.



子类.prototype

= 父类.prototype(不能直接使用)


Function inherit (c,p) {

c.prototype = p.prototype ;

}

共享了同一个原型,先说说最致命的缺点吧


1. 首先,你需要将需要继承的属性放在父类的原型中,自身的属性是不需要被继承的。


2. 然后,既然你要将两个原型放在一起,他们便共享了同一个原型,对任何一方的修改都会改变对方。



由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。

现在,我们先将Animal对象改写:

function Animal(){ }

Animal.prototype.species

= "动物";

然后,将Cat的prototype对象,然后指向Animal的prototype对象,这样就完成了继承。

Cat.prototype =

Animal.prototype;

Cat.prototype.constructor

= Cat;

var cat1 = new

Cat("大毛","黄色");

alert(cat1.species); // 动物

与前一种方法相比,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。

所以,上面这一段代码其实是有问题的。请看第二行

Cat.prototype.constructor =

Cat;

这一句实际上把Animal.prototype对象的constructor属性也改掉了!

alert(Animal.prototype.constructor);// Cat


这种方法肯定是不能直接使用的,只是作为理解放在这里。


子类.prototype

= 父类.prototype +空类型中转 + 基类.call(派生类,参数) 一起用完美解决



非构造函数的继承作为了解吧


----------------------------------------------面对对象结束----------------------------------------------------------



匿名函数和闭包


没有名字的函数叫做匿名函数


闭包是可访问一个函数作用域离变量的函数,这么理解,函数里头有一个函数,外层函数和内层函数


Function (){

Varbox = 10;


 Return Function (){

Return

box; //BOX的值可以在外部访问到了

}


}


结合变量的作用域,可以总结:作用域从下而上,从内到外可以,但从上往下,从外到内是不可以的。

function wai(){   var box1 = 10;    function nei(){        var box2 = 20;   }    window.alert(box2);//外部函数访问内层的函数是不可以的}wai();alert(box1);//在全局window环境下,访问外部函数内的变量也是不可能的


上头不是个闭包,你要区别函数内有个函数和闭包之间的区别


函数内有个函数,仍然是最基本的变量作用域


但是闭包改变了变量的作用域,让内部变量可以在外部访问,这就是闭包的意义!!





思考:我如何让外层函数访问到内层函数内的box2,然后顺着再让它也能让全局范围内访问的到

function wai(){   var box1 = 10;    function nei(){        var box2 = 20;        return

function(){           return box2; //外部函数就能访问到box2了       }    }    alert(nei()());   var box3 = nei()();//将这个box2的值给了box3,box2已经可以在外层函数中访问的到了    return

function(){       return box3;//window下就能访问到box2了    }}alert(wai()());



原来闭包就是这样,打破了原本的变量作用域,你只有理解了变量的基本作用域的同时才能理解为什么需要闭包?就是为了让外层也能访问的到内层的变量。







匿名函数是为了闭包


单一的匿名函数怎么执行?


[if !supportLists]1.  [endif]1.赋值给一个变量,通过这个变量名()来执行

[if !supportLists]2.  [endif]2.(匿名函数)(传递给匿名函数的参数)来执行



事实上,匿名函数的主要作用还是包含在一个函数内:


//函数里放一个匿名函数


Function box(){


Return function(){


Return ‘lee’;


}

}


Alert(box()());

//如果要执行的话,就需要两个圆括号



//第二个方法调用

Var b= box();

Alert(b());




闭包的作用:你能通过在函数内再创建一个函数,返回它的值,来在函数外访问到函数内的值,默认情况下,函数外是无法访问到函数内的值的。但,函数内是可以访问的到函数外的值的,这个跟我们理解的从内到外访问是可以的,从外到内访问是不行的是一致的。


function box(){   var user = 'lee';   return function(){        return user;   }}alert(box()());var b = box();alert(b());


按道理来说,匿名函数是可以访问到user的,因为从内访问外部的是可以的


那么,我们返回这个函数不就能在外部访问到这个值了吗?


它通过了返回一个函数的模式,访问到了函数内部的变量。


初步理解:


因为这个函数的返回值是一个函数,所以,我们将这个返回值函数赋值给b,那么这个函数就有了一个新的名字,不再是匿名函数了,然后,我们再来调用这个b函数



那么box()()这种形式是无法完成累加这种功能的,因为你每次这么调用的话,box里头的所有的内容都会被执行一遍,如果你b=box(),却只会执行这个匿名函数的部分。


function box(){   var age = 100;   return function(){        age++;        return age;   }}var b = box();alert(b());






再看一个循环的例子

function box(){    var arr = [];    for(var i=0;i<5;i++){         arr[i]=function(){             return i;         }    }     //如此调用,循环结束后4++=5.然后返回arr.所以结果是5,也就是说return i 的值永远是5     return arr; }var b =box(); //这一步我们获得了return arr的值 for(var i=0;i<5;i++){    alert(b[i]()); }



function box(){    var arr = [];    for(var i=0;i<5;i++){         arr[i]=(function(num){             return num;         })(i);    }     return arr; }var b =box(); for(var i=0;i<5;i++){    alert(b[i]); }


这种方法就可以?其实还是不太理解


闭包里头的this


先看下各种情况下,this的指向


var name='闫岐';var age=100;function box(){   alert(this.name);}box();


这里, box()只是个函数,函数内的this指的是windows对象(全局)


var name='王娇娇';var age=100;function box(){   this.name='闫岐';   this.age=200;   alert(this.name);   alert(this.age);}var box1=new box();box1();



new过之后,box就是个构造函数了,构造函数内的this就是对象本身

 var a=0;function box1(){         var a=10;   function box2(){        var a=20;        alert(this.a);   }    box2();   alert(this.a);} box1(); alert(this.a);



这种情况,函数发生了嵌套,仍然没有破坏this的指向,仍然是window



也可以这么理解,如果这个函数绑定到某个对象上,那么this就指向这个对象,否则就是window




那么针对对象 ——闭包里头的this会怎么样呢?这也是唯一值得思考的一个问题




var user = 'The

window';var box = {   user:'the box',   getUser:function(){        return

function(){           return this.user        }   }}alert(box.getUser()());


闭包里头的this指向了window


第一种方法:对象冒充


var user = 'The

window';var box = {   user:'the box',   getUser:function(){        return

function(){           return this.user        }   }}alert(box.getUser().call(box));


第二种方法:


var user = 'The

window';var box = {   user:'the box',   getUser:function(){        //这里的作用域this是box        var that = this;        return

function(){           //这里的作用域this是window            return that.user        }   }}alert(box.getUser()());







我们来看一个更加诡异的问题,call,apply的用法让我们去理解下函数内的this


在上面的例子中,函数内的this的指向都是window,这是因为,我们是在最常规的方法下分析得到的,我们在JS中,一切皆为对象,而JS中的函数恰恰也是一个对象。


在Java或者C/C++等语言中,方法(函数)只能依附于对象而存在,不是独立的。而在JavaScript中,函数也是一种对象,并非其他任何对象的一部分,理解这一点尤为重要,特别是对理解函数式的JavaScript非常有用,在函数式编程语言中,函数被认为是一等的。

函数的上下文是可以变化的,因此,函数内的this也是可以变化的,函数可以作为一个对象的方法,也可以同时作为另一个对象的方法,总之,函数本身是独立的。可以通过Function对象上的call或者apply函数来修改函数的上下文:

call和apply通常用来修改函数的上下文,函数中的this指针将被替换为call或者apply的第一个参数

//定义一个人,名字为jack

var jack = {

    name :"jack",

    age : 26

}

//定义另一个人,名字为abruzzi

var abruzzi

= {

    name :"abruzzi",

    age : 26

}

//定义一个全局的函数对象

function printName(){

    return this.name;

}


//设置printName的上下文为jack, 此时的this为jack

print(printName.call(jack));

//设置printName的上下文为abruzzi,此时的this为abruzzi

print(printName.call(abruzzi));


print(printName.apply(jack));

print(printName.apply(abruzzi));



只有一个参数的时候call和apply的使用方式是一样的,如果有多个参数:

setName.apply(jack, ["Jack Sept."]);

print(printName.apply(jack));

setName.call(abruzzi,"John Abruzzi");

print(printName.call(abruzzi));


改变了this的指向。这是普通函数做不到的。

再看一个例子加深印象

<input type="text" id="myText"   value="input text"><script>functionObj(){this.value="对象!";}varvalue="global变量";functionFun1(){alert(this.value);}window.Fun1();//global 变量Fun1.call(window);//global 变量Fun1.call(document.getElementById('myText'));//input textFun1.call(newObj());//对象!</script>


一般的调用是“对象调用函数”,但call aply 是“函数调用对象”。

functionmyFuncOne() {

    this.p = "myFuncOne-";

    this.A = function(arg) {

        alert(this.p + arg);

    }

}


functionmyFuncTwo() {

    this.p = "myFuncTwo-";

    this.B = function(arg) {

        alert(this.p + arg);

    }

}

function test() {

    var obj1 = new myFuncOne();

    var obj2 = new myFuncTwo();

    obj1.A("testA");                       //显示myFuncOne-testA

    obj2.B("testB");                        //显示myFuncTwo-testB

    obj1.A.apply(obj2,["testA"]);          //显示myFuncTwo-testA,其中[ testA”]是仅有一个元素的数组

    obj2.B.apply(obj1,["testB"]);          //显示myFuncOne-testB,其中[ testB”]是仅有一个元素的数组

    obj1.A.call(obj2, "testA");             //显示myFuncTwo-testA

    obj2.B.call(obj1, "testB");             //显示myFuncOne-testB

}

test();


函数.call(对象),改变函数内的上下文,this的指向发生了变化,指向对象


对象1.call(对象2),对象2继承了对象1中所有的基础方法和属性,不包含原型


对象1.函数.call(对象2),改变对象1中的this指向,指向了对象2




function inner(){

    alert(arguments.callee);//指向拥有这个arguments对象的函数,即inner()

    alert(arguments.callee.caller);//这个属性保存着调用当前函数的函数的引用,即outer()

}

function outer(){

    inner();

}

outer();


在函数内部,arguments.callee该属性是一个指针,指向拥有这个arguments对象的函数;

而函数对象的另一个属性:caller,这个属性保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容