October 5, 2016 · 43 字 · 1 分钟
工作之后,心血来潮,在京东下单了一台灿坤的意式家用半自动咖啡机。自己本身是一个很喜欢喝咖啡的人(一天离开咖啡就浑身不自在),但是经常喝速溶咖啡肯定对健康是不好的,喝星巴克又费钱,所以就入了一台咖啡机。现在已经爱上做意式咖啡这一过程了,同时对自己做的咖啡真心是喝不腻的。
苦于没有人教授,买了咖啡机两个月来,从来没有成功的到达拉花这个步骤。因为每一次奶泡总是打不好,奶泡总是过厚,奶泡和奶不能很好的融合,导致奶泡不够绵密,不够绵密的奶泡是无法进行拉花的,倒出来就是一坨坨的泡沫。趁着国庆这个假日,终于有时间能够好好研究下奶泡的打法,皇天不负有心人啊,下午终于成功了一次,打出来的奶泡是绵密的,很适合拉花(然而拉花失败了,没有把握好注入点)。
现在呢,总结下,打奶泡的过程吧,不要轻视这一过程,原本我也只是以为有个机器,只要打出奶泡就行了,什么事都是一键生成。这两个月随着一盒盒的牛奶钱付之东流,我才明白世界原来不是那么简单的(哭)。打个奶泡也是需要学习的,这真是充分说明了学习能力的重要性~看了那么多视频教程和原理讲解,总算是明白了,心疼前期的二逼打泡技术(还有我的牛奶和咖啡豆钱 T T)。
首先,硬件是必须元素:
意式半自动咖啡机。 咖啡机预热充足,放出蒸汽管中残留的水分,同时蒸汽管的水蒸气稳定 一个 350ml 的拉花缸(取决使用牛奶的量) 其次,原料也是很重要的:
必须使用脂肪含量大于 3.0%的纯牛奶(也叫全脂牛奶) 牛奶在 5 摄氏度下为益(当然不要低于 0 摄氏度) 牛奶多于拉花缸的 3 分之一 然后,打泡的手法:
蒸汽管起先保持在液面上(相切),并且倾斜于液面,蒸汽管的注入点为中心稍微靠人这一侧(不可贴缸壁),斜右上方 45 度靠杯缘处 打开蒸汽,蒸汽管发出嘶嘶的尖叫声(不要吓到,这是没问题的~),这个过程叫做发泡,此时要保证牛奶是在奶缸中小幅度旋转(不是的话,就是注入点错了),这个时候牛奶中的奶泡会越来越多,但是很多都浮在奶泡表面,得到自己想要的奶泡厚度后进入下一步骤(此时牛奶的温度大约 40 摄氏度)。需要注意的是,如果蒸汽管离奶平面太高,奶泡会变巨大;蒸汽管插入太深,就不会有奶泡,只是用花哨的技术加热了牛奶…… 等到奶泡到达你需要的量时,或者温度接近 40 摄氏度,将蒸汽管深入牛奶中,大概 1cm 的深度,此时进入打绵过程。这一过程,主要是让浮在表面的奶泡和底下的牛奶充分融合。注入点稍微往中间靠,依旧 45 度角倾斜,使牛奶旋转起来!此时正确的场景是这样的,牛奶不停的旋转(不跳跃),表面的奶泡在旋转下进入漩涡中间,逐渐消失,奶泡融入了牛奶中。这个过程是没有嘶嘶声的,等到牛奶温度到达 65 摄氏度左右,即可停止了。 最后,还没有结束的,打完的牛奶,呈现的是表面光滑,基本看不见泡泡。通过轻微晃动奶缸,让牛奶和奶泡更均匀,通过奶缸轻震桌面,让大泡泡消失。到这个时候,奶泡就真的完成了。
注意点是:
奶泡和奶是千万不能分离的,要融合 注入点一定要使得牛奶旋转!旋转!旋转!不许跳跃!发泡和打绵都要旋转!!全程旋转,奶泡基本成功~ 可以使用水,滴入洗洁精,观察注入点的流动方向(穷人福利)
September 24, 2016 · 26 字 · 1 分钟
工作职责:
作为 KM 平台中心开发组的员工,这段时间主要负责 KM 外网产品“乐享”的前端开发工作。其中涉及的工作为 PC 端的页面开发、移动端的 H5 开发、共有组件的开发和抽取等需求。主要表现为利用 jQuery、Seajs、LESS 等技术进行相关页面的数据处理,交互逻辑开发,共有组件的开发;利用 Laravel 等技术进行后台的业务逻辑的开发和维护。
工作成果(主要以开发模块做总结):
个人空间模块的前端和后台开发 公司首页、K 吧首页、论坛首页的页面改版 共有组件的开发和抽取 不足之处:
与设计师在设计稿的出稿时间上沟通不足,导致开发时间分配紊乱 对需求的一些边界条件考虑不够齐全,出现有时开发中出现重构的问题 改进计划:
分到需求后与设计师提前沟通好出稿时间,对设计工作量大的需求进行开发时间的隔离,避免设计工作量大的需求排在一起,优先级高的需求要优先处理 多与产品进行需求上的沟通,多从产品的角度去思考需求的潜在问题 每天适当空出时间进行新技术的学习
August 15, 2016 · 939 字 · 5 分钟
函数的扩展 # ES6 对函数进行了扩展,新的函数支持定义默认值、扩展运算符、写法更加简洁的箭头函数等。
函数参数的默认值(3 种形式) # 1.函数默认值
let fn = function (x = 1, y = 2) { return x + y; }; fn(); // 3 // 代替函数体内判断的写法 let fn = function (x, y) { x = x || 1; y = y || 2; return x + y; }; fn(); // 3 2.函数默认值——解构参数
可以通过解构变量来定义函数参数的默认值,减少参数的个数
let fn = function ({ x = 1, y = 2 }) { return x + y; }; fn(); // 3 3.函数默认值与解构赋值结合
注意:前面这两种方法都存在一个问题,如果只使用第二个参数,就必须给第一个参数传入undefined来指示第一个参数的默认值,通过解构赋值和函数默认值结合的形式可以避免这样的问题。
// 既可以不传入参数,也可以指定参数调用 let fn = function ({ x = 1, y = 2 } = {}) { return x + y; }; fn({ y: 2 }); // 3 实例:默认参数与解构赋值混合使用
let fn = function (x = 1, { y = 2, z = 3 } = {}) { return x + y + z; }; fn(5); // 10 rest 参数与拓展符 # ES6 也支持了函数 rest 形式的参数(使用者不需要考虑参数的个数),rest 参数必须跟在函数最后面,实例如下:
August 7, 2016 · 431 字 · 3 分钟
具体的 ES6 语法,可以参考阮一峰老师写的《ECMAScript 6 入门》。 该文章只整理一些实战中很有用的例子。
let&const # let 块级作用域变量声明 # let基本修复了以往var声明的弊端——变量全局污染,变量声明只在块级作用域中有效,不可重复声明,ES6 同时新增了新的块级作用域{},一些实用的例子如下:
1.代替原有的闭包创建的块级作用域
{ // code } // 代替IIFE写法 (function () { // code })(); 2.解决 for 循环索引变化问题
// 循环结束i被垃圾回收 for (let i = 0; i < 6; i++) {} // 循环结束i的值为5 for (var i = 0; i < 6; i++) {} const 常量声明 # const解决了没有常量定义的问题,定义后的常量不能在修改,不可重复声明,同样的,常量声明只在块级作用域中有效,实用例子很简单:
订阅-发布模式 # Backbone.js最吸引人的基本上就是事件对订阅-发布模式(pub/sub)的使用了,一个典型的Backbone例子如下:
// 新建一个model var Person = Backbone.Model.extend({ ... }); var person = new Person({}); // 绑定人名变更事件(订阅) person.on('callme', function () { alert('wayne'); }); // 修改人名,触发事件(发布) person.trigger('callme'); // 此时会弹出'wayne'的信息 // 解除事件绑定(取消订阅) person.off('callme'); person.trigger('callme'); // 什么都不执行 有了这个模式,我们就可以做到一些比较节省工作的事情,一个典型的应用场景——在线笔记:
用户在笔记的其中一个分类 backbone学习 点击添加一个新的笔记并保存 保存成功,笔记总数+1 使用jQuery的大致实现过程如下:
$('#save-btn').click(function() { // 保存笔记 $.ajax(function() { success: function() { changeCount(); } }); };); 看着似乎没什么问题,但是如果需求改变了,比如变成这样:
用户在笔记的其中一个分类 backbone学习 点击添加一个新的笔记并保存 保存成功后,笔记总数+1、笔记编写界面刷新、本地笔记缓存同步、服务器缓存同步、自动分享到团队笔记中。。。 这个时候代码会变成这样:
$('#save-btn').click(function() { // 保存笔记 $.ajax(function() { success: function() { dataView.changeCount(); noteView.refresh(); local.syncNote(); server.syncNote(); share.toMyTeam(); ... } }); };); 此时,各模块的代码严重耦合在了一起,写这段代码的人同时需要了解其他模块有什么函数,其他模块的编写者也不能随意更改自己的模块接口。
学校附近一家店有一个外卖小哥,年龄跟我差不多,二十出头,留着军队小兵的平头,皮肤是典型的东方黄,不偏不倚,大眼睛,浓眉毛,身高跟我也差不多,一米七几,说高不算,说矮也勉强,中等身材,约莫 120 斤,130 斤。
小哥平日里负责的是送外卖,主要服务对象也是学校里的学生,每天骑着一辆小电驴,电驴上驾着一泡沫箱装着的外卖,呼呼地穿梭于这几个校区之间。
可能是我名字比较特别,第二次点这家的外卖时,在宿舍楼下见到小哥,像往常一样报了宿舍名。小哥拿起写着我名字的那份,皱了下浓浓的眉毛。
“郑伟柏是吧?你的外卖。"。
“额……对”。
习惯于别人总是叫错我的名字了,我只是愣了下,并没有去更正小哥的话,我想叫错就错吧,也就拿下外卖而已。
接下来的日子里,说来也奇怪。小哥就这样记住了我错误的名字,隔了好长一段时间点他家的外卖,小哥每次送过来,电话接起,对面那头是直接喊起我的名字了。“郑伟柏,你的外卖,麻烦来楼下拿一下!",“郑伟柏,你外卖到了”,“郑伟柏,外卖在楼下!"……
小哥不知咋地,叫我名字特勤快,每次隔了大老远,骑着小电驴,就叫我的名字,看了那么多次订单上的我的名字,却没有一次改正叫法,这让我觉得很是有趣。小哥每次都笑的很开心,远远的就看见他咧着大嘴,露出一口白牙,骑着小电驴跑上斜坡来,丝毫
没有因为这份日复一日,年复一年的枯燥工作心烦过。
临近毕业了。这几天下雨,又叫了他家的外卖,想想这应该是最后一次叫他家的外卖了吧!八点钟点的外卖,小哥 8 点 30 分来了电话,依旧是那熟悉的开头。下了楼,不见小哥的身影。过了两分钟,小哥穿着件旧蓝色的雨衣从斜坡上慢慢跑了上来,左手提着我的外卖,大老远的依旧是标志性的表情——咧嘴笑。
“哈哈,我电动车在斜坡下骑到一半没电了!郑伟柏,你的外卖啊!"。
“那你咋回去啊现在,这下大雨的,那电动车可以直接脚踏吧?"。
“哈哈,可以啊,我慢慢骑回去,就你一份而已,无所谓无所谓,送完就下班了都。得了得了哈,我下去牵车走了,再见再见”。
“嘿小哥。"。
“咋啦咋啦?"。
“我名字叫 ABC(这里代指三字姓名),不叫 ACB”。
“嘿,好好好,原来我老叫错了啊,ABC,不管不管,ACB 叫熟了都”。
小哥晃晃悠悠的跑下了斜坡,过会儿渐渐消失在雨中。
再见啦,有趣的外卖小哥。
April 11, 2016 · 229 字 · 2 分钟
PDO 提供了一个PDOStatement::debugDumpParams — 打印一条 SQL 预处理命令的调试方法,但是打印出来的结果有点鸡肋,比如说:
<?php try { $pdo = new PDO(); $sql = "SELECT * FROM `users` WHERE `username` = ? AND `email` = ?"; $stmt = $pdo->prepare($sql); $stmt->bindParam(1, $username, PDO::PARAM_STR); $stmt->bindParam(2, $email, PDO::PARAM_STR); $username = 'Saul Goodman'; $email = '********@qq.com'; $stmt->execute(); $stmt->debugDumpParams(); } catch (PDOException $e) { echo $e->getMessage(); } // 结果很感人: // SQL: [58] SELECT * FROM `users` WHERE `username` = ? AND `email` = ? // Params: 2 // Key: Position #0: // paramno=0 // name=[0] "" // is_param=1 // param_type=2 // Key: Position #1: // paramno=1 // name=[0] "" // is_param=1 // param_type=2 ?> 要是 SQL 语句一复杂这根本就阅读不了嘛!
February 27, 2016 · 52 字 · 1 分钟
原生 js 轮播组件的代码在这:https://github.com/wynn719/study-baidu-ife/tree/master/task0002/task0002_3
之前做的时候,会发现轮播在浏览器可见的情况下(没有缩小,没有切换标签),轮播是正常运行的;而当浏览器不可见的情况下(缩小, 切换标签),重新打开页面,轮播会出现几秒钟抽搐(真的很像)的现象,然后又恢复正常运行。
排除了动画逻辑,定时器泄露的问题,偶然情况下,发现 IE 下居然没问题!
看来崇拜的 chrome 也有抽筋的时候,**即 chrome 在浏览器不可见的情况下(缩小, 切换标签),会把定时器挂起(不会在后台运行),因此重新打开时,挂起的 n 个定时器突然运行,**所以 chrome 就抽风了!
说了这么多,解决方法很简单,只要监听这个事件 visibilitychange :
// fix chrome下的bug,定时器切换选项卡时被挂起 document.addEventListener("visibilitychange", function () { if (document.visibilityState == "visible") { slide(); // 未挂起时执行轮播 } else if (document.visibilityState == "hidden") { clearInterval(timer); // 挂起时清除定时器 } }); 问题就解决了~
demo 展示在这:http://wynn719.github.io/study-baidu-ife/task0002/task0002_3/task0002_3.html
(可恶,这个问题居然困惑了我好久,气煞我也,所以要记录下来……)
December 31, 2015 · 6 字 · 1 分钟
从14年下半个学期开始,养成了读书的这个习惯,从一开始的厌倦到现在的欲罢不能,非常感谢昔日的自己能够坚持这个好习惯,并且让这个习惯成功的转型成了爱好。
以前从来不懂得读书的好处,“读了10本书后,你会以为身边不读书的人都很愚蠢;读了50本书后,你慢慢地发现自己才是最愚昧无知的人;你读的书越多,就越认识到自己的不足~”,这是在某个地方看到的,现在觉得真的很有道理。
既然是读书小结,那就不要太多废话了,直接贴图吧,感谢大神们做出了一个如此好用的豆瓣统计!
小结 # 今年读的书有点多啊,70本,读下来觉得似乎有点过了,接下来每年应该保持50本就差不多,不然吸收不了啊!再接再厉!
December 19, 2015 · 166 字 · 1 分钟
最近读了一本书,松浦弥太郎的《今天也要用心过生活》,书里的道理总是很简单,印证那句老掉牙了的话:道理我都懂,但依然过不好这一生。突然想到,道理明明很简单,我缺乏的是主观能动性吧。生活本可以常驻新鲜,在看不到的地方保持自己的天真,curiosity,小孩子的天性,人长大后就没了,人也衰老的越来越快,心也就变硬了。
2015年快过去了,不管过的好不好,2016年终将来临。我想借由这本书的内容审视下2015年的自己,2015年所做得不好的地方要一一对自己指出,然后给2016年的自己好好指点一下迷津,让2016年的我活得更加有意义。
以下是书的内容和对自己的审视:
Chapter 1 健康的早餐 # 每天的自我改造
2015:会尝试着学习一些小知识,小技能来丰富自己的生活,但持续性不好,一年下来不到100天
2016:一天中花时间学习小的爱好,争取做到持续时间大于150天
“早安”的功效
2015:经常会回避那些自己不喜欢接近的人(除去一些因为三观不合而不像认识的)
2016:多尝试主动与他们沟通,减少尴尬
让自己从容的一小时
2015:经常给自己独处的空间,这个做得挺好
2016:可以把这一小时从早起中抽出,继续保持
发现快乐,用心下功夫
2015:缺乏在小事中找快乐的能力
2016:如果感到做这件小事能够开心,那就尽管去做吧
发现全新自己的捷径
不认同,略过。
清洁的自信
2015:做得挺好的
2016:继续保持
起而行的行动家
2015:经常犹犹豫豫,但事后发现事情明明可以很快就做好决定,而且收效更好
2016:忌讳犹豫不决,从简单的事情开始做,小事在10分钟之内决定好,大事在1个小时到半天内决定好
自在的节奏
2015:对自己的生活节奏和工作节奏把握的不是很好,多半是因为情绪的波动导致自己节奏被打乱,没有注意合作伙伴的节奏,比如有时候容易打扰到别人,没有事先沟通好交流时间
2016:需要注意调整自己的节奏,同时也要注意合作伙伴的节奏,切勿心急
用心吃饭
虽然没有自己亲手去做饭,但一直很用心的吃饭,略过
优雅的握筷方式
不认同,略过
干净的姿态
2015:作为一个coder,虽然仪表是弄得比较整洁,但是姿态总是不够干净利落
2016:保持仪表整洁干净的同时,要多健身多锻炼,保持姿态的自然,落落大方,多注意不要弯腰驼背,坐下时不要老是头前倾
遇见“老师”
2015:遇到一些行为举止不雅的人,太容易意气用事了
2016:把那些你所厌恶的人和行为当作你自己的“老师”,有则改之,无则加勉,看不惯的东西,笑笑而过就好了
让笑容为生活保鲜
做得挺好的,继续保持
今天也要用心生活
2015:每个人都从内心里抗拒新事物的发生,抗拒面对新事物,有时候我会很喜欢去尝试新事物,但有的时候还是太畏畏缩缩,容易因为情绪失控而厌倦面对新事物
2016:年纪越大,越要有勇气挑战新事物,从小事调整开始,尝试着去学习身边每一个细微的事
Chapter 2 上乘的午餐 # 分享喜悦
November 16, 2015 · 89 字 · 1 分钟
废话不多说,直接讲一下常见的单行截断:
<div class="single-line"> For a while I think we have got away with it, but then a flare goes up and we are caught suddenly in broad daylight. </div> .single-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 200px;/* 给一个宽度限制就可以出现...了 */ } 这个方法在各大浏览器都适用,然而当遇到需要多行截断时问题就来啦,除了粗暴的用后台来根据指定字数truncate掉,或者粗暴的写个jstruncate掉,其实还可以用纯css的方法来实现:
<div class="multiple-lines"> For a while I think we have got away with it, but then a flare goes up and we are caught suddenly in broad daylight. </div> .multiple-lines { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; width: 200px; /* 一样要给一个宽度 */ } 唯一不足的是,如代码所见,对webkit内核的支持比较好,因此在移动端的兼容性会比较好(移动端大部分浏览器都是webkit内核)
October 7, 2015 · 95 字 · 1 分钟
从后台返回的数据经常是以数组的形式传递过来的,开发中遇到的一个小问题,js在处理数组删除时很灵活,故记笔记。
for循环删除数组的某一项,一般的会马上这么写代码:
for(var i = 0, len = array.length; i < len; i++){ if (array[i] === number) { array[i].splice(i, 1); } } 但是这样是不行的,因为数组的长度已经改变,这样会造成数组越界,这时可能会想到这样写:
for(var i = 0; i < array.length; i++){ if (array[i] === number) { array[i].splice(i, 1); } } 同样也行不通,因为数组的索引变化难以把握
正确的做法:
for(var i = array.length - 1; i >= 0; i--) { if(array[i] === number) { array.splice(i, 1); } } 这样可以保证数组 splice 后,循环还按正常的逻辑运行。
当然,如果想保证索引不改变的话,可以使用 ES5 的新操作符
for(var i = 0, len = array.length; i < len; i++){ if (array[i] === number) { delete array[i]; // 索引逻辑不受影响哦! // 需要浏览器支持的话,可以尝试把`array[i]`置为null // array[i] = null; } }
September 5, 2015 · 87 字 · 1 分钟
开发中经常会碰到的一种情况是:由于页面的内容不够长,无法撑开页面,导致底部的模块下面留下一大块空白,实在不雅,sticky footer 技术解决了这个问题
1.页面结构:
<div class="page-wrap"> <div class="header">header</div> <div class="content"> <button id="add">Add Content</button> </div> </div> <div class="site-footer">footer is!</div> 2.css:因为此处使用到了 after 伪元素,所以也就意味着 IE7 以下是无法使用该方法的
* { margin: 0; padding: 0; } html, body { height: 100%; /* 注意,这是必须的 */ } .header { height: 100px; background-color: #ffff80; } .page-wrap { min-height: 100%; margin-bottom: -150px; /* 必须与footer高度相等 */ } .page-wrap:after { content: ""; display: block; height: 150px; } .site-footer { height: 150px; } .site-footer { background-color: #0080ff; } 3.原理:
margin-bottom 将 footer 向上拉动 :after用于当页面长度超过屏幕时占位把 footer 向下挤 footer 必须位于最外层,这也 DOM 结构有所不规范
September 3, 2015 · 9 字 · 1 分钟
转眼间,我已经在腾讯实习了两个月了。
回想起今年4月,BG叫我一起去报腾讯的校招,我当时很犹豫,总觉得自己的能力还不足以去腾讯实习。“去看一看笔试考什么也好啊,就当做是练练胆,为以后做准备嘛。”,当时BG是这样跟我说的,然后我就抱着看笔试题的心态跑去笔试了(汗……)
4人的小分队跑去腾讯笔试,全部都通过了笔试,其实我笔试写得不怎么好,也不知道是为什么就过了……一面的面试官,也就是我现在的leader,面了有30分钟,全部都问的是前端的知识,可能跟我的简历有关吧(简历上除了前端相关的就没有别的了,还要说一句,简历真的是粗制滥造啊,跟旁边一群简历弄得漂漂亮亮的小伙伴们比起来真是相形见绌啊!=_=),面完后的第一感觉就是–结束了。当时还很庆幸有过来面试,leader给了我很多前端学习的建议。然而,过了好几天,居然被通知面试过了,当时没有很开心,反而是更担忧了……
二面,4人小分队只剩下3个人,雪糕弟掉队了~二面之前,我翻了翻网上别人写的腾讯二面经历,妥妥的技术面,全方位知识各种考验。可是二面的面试官却出乎了我的意料,到了之后,没怎么问技术的问题,只是问了我对前端的认识,我就噼里啪啦的说了一堆。30分钟的面试,剩下的5分钟都是在问我能够实习多久的事,真的吓坏我了!我还是以为结束了,然而,过了好几天,又被通知三面。
三面只剩下2个人了,就是我和BG。HR面,聊人生,聊思想,聊性格,聊未来……是的,HR面就是在聊天,HR人很好,见我有点紧张,一进来就帮我拿书包和推椅子,是的,我更紧张了~HR在我临走之前还是给了我一些鼓励,让我很惊讶,当时就觉得腾讯的人真的好好,情商都好高。
剩下的日子,很多小伙伴都是在担忧中度过。对于我,其实挺无所谓的,毕竟面试给我带来了挺多的知识,或许是这种淡然的态度,给我迎来了这次实习的机会。
很凑巧,最后一轮面试,只有我通过了,每一轮走一个小伙伴,真的让我很不舒服,很希望能更他们一起实习。
转眼间在腾讯实习了两个月,又到了腾讯校招的时候了,小伙伴们也重新踏上了这条路,有时候会想,要是当时我没有去报名,那今天的我也不会有这么大改变,或许还是默默无为~真的是验了一句话——不逼自己一把,永远不知道自己有多厉害。
每年母上总会去帮四个孩子求签,而我每年都有四个字——戒骄戒躁,望自己能践行。
constructor 是什么 # 当我们创建一个构造函数时,就会创建一个 constructor 的属性
function Foo() { this.name = "Jimmy"; } // 实例化对象 var foo = new Foo(); console.log(Foo.constructor); // Function() console.log(Foo.prototype.constructor === Foo); // true console.log(foo.constructor); // Foo() console.log(foo.constructor === Foo); // true 由于 Foo 本身是由 Function 创建的,所以 Foo 的 constructor 就自然而然的指向了 Function(),而 foo 是由 Foo()创建的,所以 foo 的 constructor 就指向了 Foo,即 constructor 默认指向创建自己的函数。
要注意的是,Foo.prototype.constructor 也指向 Foo,其实就是一个循环引用,Foo 的 prototype 属性指向 Foo 的原型,然后 Foo 的原型的 constructor 属性指向 Foo
即:Foo.prototype -> Foo 原型,Foo 原型的 constructor -> Foo
javascript 不具有其他语言的类,继承等的特性,因此 javascript 的面向对象编程更多的是基于构造函数和原型的方式实现类的功能;基于原型链来实现类的继承;基于闭包的原理来实现私有化;
当然也有非原型链的继承,如 jquery 中使用的对象拓展(extend),通过对对象的 深度复制来实现。
javascript 面向对象编程 # javascript 本身不具有类的特征,那如何模拟类的特征呢?
一些好的尝试,但不够完美 # 在此之前,有工厂模式,有构造函数模式,但都存在着一些大的问题:
工厂模式的问题:无法识别对象是由谁创建的 构造函数的问题:在创建每个实例时,构造函数给每个实例的新建了方法,即使该方法本身是一模一样的,而面向对象的思想并不希望方法是一样的但是却要重复的创建,这本身浪费内存空间 原型模式 # 为了解决工厂模式和构造函数模式的问题,于是有了原型模式
把共享的变量和方法都放到原型上,然后让子类的原型与父类的原型建立关系,就实现了原型式继承
代码如下:
function Person(){} // 定义要共享的方法 Person.prototype.name = 'Jimmy'; Person.prototype.sayName = function(){ console.log(this.name); } // 或者另一种写法 Person.prototype = { constructor: Person, // 不要忘记修正constructor指向 name : 'Jimmy', sayName = function() { console.log(this.name); } } 但是原型模式也存在着一些问题:
学习任务来自:百度ife前端技术学院
学习资料参考自:
闭包
深入理解javascript原型和闭包
在经过前面作用域和原型、原型链的学习,感觉遇到闭包也不怕啦… 准备被虐杀!
先补全一些作用域上的知识:
自由变量 # 在A作用域中使用的变量x,却没有在A作用域中声明(即在其他作用域中声明的),对于A作用域来说,x就是一个自由变量。
例如:
var x = 10; function fn(){ var b = 20; // x不在fn的作用域中声明,但却调用了 // 因此x叫做自由变量 console.log(b + x); } 一个经常容易犯错的例子:
var x = 10; function fn() { console.log(x); } function show(f) { var x = 20; fn(); } show(); // 10 这个例子中,输出的不是20,而是10,因为在函数定义的时候就已经决定了函数的上下文执行环境,故,fn的作用域链上,只能找到window执行环境下的变量x
学习任务来自:百度 ife 前端技术学院
学习资料参考自:
JavaScript 探秘:强大的原型和原型链
理解 JavaScript 原型
深入理解 javascript 原型和闭包
个人注:主要是区分Prototype,prototype,__proto__,[[Prototype]],理解constructor
Prototype:原型本尊 prototype:原型属性,指向Person,实例中不存在 __proto__:原型访问器,指向 创造该对象 的原型 [[Prototype]]:等同于__proto__ constructor:构造函数,指向函数本身(如 new Person),constructor.prototype也指向Prototype javascript 原型和原型链 # 对象中的proto属性(隐式原型,javascript 并不希望我们访问到该属性) # 每当创建一个对象,该对象就会有一个__proto__属性,该属性指向创建该对象的函数的原型
学习任务来自:百度ife前端技术学院
学习资料参考自:
28 May 09 Javascript作用域原理
JavaScript 开发进阶:理解 JavaScript 作用域和作用域链
javascript的作用域链 # javascript中的函数运行在她们被定义的作用域里,而不是它们被执行的作用域里 – 《javascript权威指南》
注意:在JS中,作用域的概念和其他语言差不多, 在每次调用一个函数的时候 ,就会进入一个函数内的作用域,当从函数返回以后,就返回调用前的作用域.
javascript中作用域的实现方式与C/C++不同,不是使用“堆栈”的方式,而是使用列表,实现过程如下(ECMA262):
javascript高级程序设计3中,指出函数的执行环境是保存在环境栈中的,执行完函数后,会把函数环境弹出栈
……为何不一样的说法啊(高程中没有探讨到[[scope]]上)
作用域链(scope chain):保证对执行环境有权访问的所有变量和函数的有序访问。
任何执行上下文时刻(context)的作用域,都是由作用域链(scope chain)来实现的。 在一个函数被定义的时候,会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性。 在一个函数对象被调用的时候,会创建一个活动对象(object),然后对每一个函数的形参(arguments),都命名为该活动对象的命名属性,然后将这个活动对象做为此时的作用域链(scope chain)最前端,并将这个函数对象的[[scope]]加入到scope chain中。 解析这个例子:
var func = function(lps, rps){ var name = 'laruence'; ........ } func(); 它的解析过程是这样的: