我的前端学习路线

我的前端学习路线

  我确认往前端发展是从2018年大三的寒假开始的,因为本科由于兴趣选择了数字媒体专业,自然而然就往前端这边靠了。要具体说一说的话原因也非常简单:JS非常简约易上手,浏览器就是调试器,还可以用代码构建形形色色的可视化页面,完美贴合我这种半计算机半艺术的专业。

  干任何事都有个起点,因为我的专业是半计算机专业,这就意味着操作系统原理、汇编、计算机网络这些CS的课程基本都没有,有了也是非常水的讲着玩玩,所以我的起点水平也就是对计算机语言的基础知识基本掌握,面向对象的原理(继承、封装、多态)了解过没怎么用过;数据结构会基本的栈、队列、排序、二叉树这些,而且还是只知道原理不会用(不过前端走到现在从没用过,基本pop(),push(),sort()什么的JS都给你封装好了);前端相关知识HTML、CSS基本标签、属性会用,JS基本语法会用,基本DOM操作会用,jQuery了解过一点。

  其它的废话我就不多说了,这里着重记录一下我的学习历程,具体分为:我的书单、网上资源。

我的前端书单(分先后顺序,书名后跟着难度和推荐度)

  我从小就不爱读书,爱看电影,因为感觉书非常无聊,不如电影动态的好看,但是到了现在要学技术了,就必须看书了,不知道大家有没有过这种体验:看动画片一集的时间看漫画能看十集,所以读书而且是读技术书对于快速增长学识非常关键。

1.《Head First HTML 与 CSS》 难度:0星 推荐度:3星

  都称作是最最基本的无脑入门书,也有一些HTML5的新内容,对于我这种零零碎碎学了些前端的人正适合当作第一本书看。

2.《JavaScript高级程序设计》 难度:2星 推荐度:5星

  结合了网上各类大神的推荐和自己的水平,没有去看犀牛书而是看了这本,总体感觉非常系统的带你巩固了一下JS,如果是小白建议就看完第13章事件,基本上就可以完成一些简单页面了,后面的内容什么时候用到了或对JS彻底入门了再去看即可(因为看到第13章JS才算摸到门把)。

3.《JavaScript语言精粹》 难度:3星 推荐度:5星

  当时看北妈的推荐读的这本,真的是如北妈所说:“读一页相当于《JavaScript高级程序设计》的十页”,看了这本书你基本就可以对原型、继承、闭包等有深刻的了解了,吃透这本小薄书我感觉JS才算真正入门了

4.《图解CSS3:核心技术与开发实践》 难度:1星 推荐度:3星

  因为只是基本的CSS了解过,对CSS3并不是很了解,所以看了这本书,但因为是14年的有时候还需要看最新的CSS参考手册自己去挨个试试,总体还不错。

5.《CSS世界》 难度:3星 推荐度: 5星

  很多的CSS属性你知道是什么,但你搭配起来会出现一堆奇奇GAYGAY的问题,所以这本书就帮你了解CSS各属性间的关系和深层用法,我第一遍看因为没什么项目经验所以很多也只是看了,但是没用过所以没有概念,以后做的东西多了再回来看收获更多。

6.《JavaScript设计模式与开发实践》

  最近正在看,看完再评价。

7.《算法》

  最近正在看,感觉还是要了解一下算法,为了找工作也好,以后实际需要也好。

我的其它书单

  学校的那些专业课的书就不说了,只提一下看过的觉得对前端这方面有点用的书单:

1.《3D数学基础:图形与游戏开发》 难度:2星 推荐度: 4星

  看这书会基本的C++和高中数学即可,起码能对计算机图形学的最基础有点认识了,能知道物体在3D空间是如何通过代码进行运动的了,CSS3的transform变形函数里的translate()、rotate()、scale()等与之相关很大。

2.《图解TCP/IP》 难度:2星 推荐度: 3星

  暂时为了以后与计算机网络打交道,先了解一下,但是因为很多名词从来没见过,看过了也就看过了,没有任何概念,所以现在还看不出来有什么用,只是对TCP/IP模型那些宏观上的东西有点感觉。   

JS的面向对象

  最近刚开始看《JavaScrit设计模式与开发实践一书》,感觉着实不错,让我对JS的面向对象有了更多的认识,下面就来总结一下:

  首先要了解的一点是:JavaScript是动态类型语言,与Java,C++这些静态类型语言不同,它无需类型检测。

多态

  多态就是将“做什么”和“谁去做以及怎样去做”分离开,也就是将“不变的事物”与“可能改变的事物”分离开来,然后把不变的部分隔离出来,把可变的部分封装起来,这样即可实现:同一操作作用于不同的对象,可以产生不同的解释和不同的执行结果。

  多态最根本的作用就是通过把过程化的条件分支语句转化为对象的多态性,从而消除这些条件分支语句。

封装

  因为JavaScript没有一些语言中的public,private,protected等关键字,所以只能依赖变量的作用域来实现封装特性,而且只能模拟出public和private这两种封装性。

  一般通过函数来创建作用域(如闭包就是实现了公开方法访问私有变量),但最新的ES6提供了let关键字,和Symbol创建私有属性,现在我还没看,等着一定会去了解。

继承

  关于原型链继承请参考博客“JS中new运算符详解”和高程6.3节(一定要先搞懂跟new有关的构造器模式,再去看原型链)

  JavaScript是一门基于原型的面向对象语言,它的对象系统就是使用原型模式来搭建的,所以它绝大部分的遵守原型编程范型的基本规则,即:

  1. 绝大部分的数据都是对象
  2. 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆
  3. 对象会记住它的原型
  4. 如果对象无法响应某个请求,它会把这个请求委托给它的构造器的原型

1. 绝大部分的数据都是对象

  JavaScript中的根对象是Object.prototype对象,它是一个空对象,所有的对象都是从Object.prototype对象克隆而来的,Object.prototype对象就是它们的原型。

2. 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它

  JavaScript的函数既可以作为普通函数被调用,也可以被new运算符作为构造器被调用,其中的new运算符实际上就是克隆了一个原型的过程。

  关于new运算符的内部具体操作请见我博客中的“JS中new运算符详解”!

3. 对象会记住它的原型

  JavaScript给对象提供了一个名为proto的隐藏属性(这是浏览器的实现,规范里应该是不可访问的[[prototype]]),该属性默认指向它的构造器的原型对象。

  所以proto就是对象跟“对象构造器的原型”联系起来的纽带。

4. 如果对象无法响应某个请求,它会把这个请求委托给它的构造器的原型

  JavaScript的对象最初都是由Object.prototype对象克隆而来的,乍一看我们只能得到单一的继承自Object.prototype的对象,但对象构造器的原型并不仅限于Object.prototype上,而是可以动态指向其他对象。

  所以我们一般通过设置构造器的prototype来实现原型继承:

1
2
3
4
5
6
var A=function(){};
A.prototype={name:"Daoma666"};
var B=function(){};
B.prototype=new A();
var b=new B();
console.log(b.name);//Daoma666

  在执行时,引擎做了什么事:

  1. 首先尝试遍历对象b中所有属性,但没有找到name这个属性
  2. 查找name属性的请求被委托给对象b的构造器原型,它被b.proto记录着并且指向B.prototype,即new A()创建出来的对象,即一个指向构造器A的prototype的对象。
  3. 然而在该对象中依然没有找到name属性,于是请求继续委托给这个对象构造器的原型A.prototype。
  4. 然后在这里找到了name属性,并返回它的值

  最后我们还要注意一点,如果在A.prototype也没找到name属性时,请求会被传递给A.prototype的构造器原型Object.prototype,因为根对象是个空对象,且原型是null,所以这次请求就到此为止,b.name返回undefined。


  现在我们就能理解ECMAScript5提供的克隆对象的Object.create方法了,它天然的实现了原型继承。

1
2
3
4
5
Object.create=function(obj){
var F=function(){};
F.prototype=obj;
return new F();
}

  即上面的代码可以这样实现:

1
2
3
4
var A=function(){};
A.prototype={name:"Daoma666"};
var b=Object.create(new A());
console.log(b.name);//Daoma666

  此外还有需要注意的几点:

  1. Object.create()来创建对象比通过构造器创建对象要慢
  2. 设置构造器的prototype实现原型继承时,除了根对象,任何对象都会有一个原型;但通过Object.create(null)可以创建出没有原型的对象。

JS中insertAdjacentHTML()方法

JS中insertAdjacentHTML()方法

  一般我们在写JS时,想在一个DOM节点中插入HTML代码,这时有三种方法:

1.使用innerHTML

  缺点是会把原先该节点的代码全部覆盖掉。

2.使用insertBefore(),appendChild()方法

  缺点一是麻烦,需要先createElement()创建一个节点,再插入内容,最后再append到DOM树中。二是性能低下,具体见:DOM appendHTML实现及insertAdjacentHTML

3.使用insertAdjacentHTML()方法,更加灵活

  insertAdjacentHTML 方法:在指定的地方插入html标签语句

  原型:insertAdajcentHTML(swhere,stext)

  参数:

  swhere: 指定插入html标签语句的地方,

  stext:要插入的内容

  有四种值可用:

  1. beforeBegin: 插入到标签开始标记前

  2. afterBegin:插入到标签开始标记之后

  3. beforeEnd:插入到标签结束标记前

  4. afterEnd:插入到标签结束标记后

CSS实现居中的方法

CSS实现居中的方法

1.使用width和margin实现水平居中

  width指定完宽度,就可以直接margin:0 auto;实现水平居中了

2.使用text-align实现水平居中

  使内联元素居中,所以要在想居中的内联元素的父元素中使用

3.使用top,left和margin实现水平垂直的居中(但不如下面第4种方法简单)

  方法就是top: 50%;left: 50%;然后margin: -所在盒子宽度/2 0 0 -所在盒子高度/2;(可用于弹窗弹出到页面正中间)

4.使用 margin:auto 实现块级元素水平垂直的居中

HTML:

1
2
3
<div class="father">
<div class="son"></div>
</div>

CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.father {
width: 300px; height: 150px;
background-color: #f0f3f9;
position:relative;
}
.son {
/*重要部分*/
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
margin: auto;
/*重要部分*/
width: 200px; height: 100px;
background-color: #cd0000;

}

5.使用line-height实现内联元素垂直居中

  如果该内联元素只有单行文本,那么连height都不用设置,直接设置一个line-height属性就可以。

  原理为line-height=行距+font-size,即行距的上下等分机制。具体见《CSS世界》一书中5.2.2节,其中还有多行文本或替换元素的垂直居中如何用line-height和vertical-align实现。

CSS属性值的百分比

CSS属性值的百分比

  经常能见到CSS属性值出现百分比,之前一直也没有整理过,现在为了以后不迷糊,遂整理一下。

百分比单位

1.乘以包含块的宽度:margin, padding, left, right, text-indent, width, max-width, min-width

  其中padding和margin的百分比值无论是水平方向还是垂直方向均是相对于包含块宽度计算的!

  text-indent的百分比值也是相对于当前元素的包含块宽度计算,而且注意要避免使用过大的负值来隐藏文字,因为会有很多弊端,设置得当即可。

2.乘以包含块的高度:top, bottom, height, max-height, min-height

  关于包含块(containing block)的概念,不能简单地理解成是父元素。

  1. 根元素(html元素),被称为“初始包含块”,其等同于浏览器可视窗口大小。
  2. 如果是static定位和relative定位,包含块由其最近的块容器祖先盒的content box边界组成。
  3. absolute定位元素相对的包含块是最近的position不为static的祖先元素。
  4. 对fixed定位的元素,它的包含块是”初始包含块”,一般为视口(viewport)。

  但如果absolute定位没有设置left/right/top/bottom属性的话,此时称为无依赖的绝对定位元素,它相对于本身的位置定位。

  绝对定位元素的height: 100%;是第一个具有定位属性值的祖先元素的高度,而height:inherit;则是单纯的父元素的高度继承。

3.乘以元素(border-box)的宽度:border-radius

4.乘以元素的字体大小:line-height

5.乘以元素的行高 :vertical-align

  vertical-align只能应用于内联元素和display值为table-cell的元素!

6.background-position中的百分比单位与CSS中其它百分比单位都不一样!具体见“前端配合设计师之background-position定位图片”这篇Blog

7.字体大小中的百分比 font-size 中的百分比值应该乘以元素所继承到的字体大小,也就是父元素的字体大小

  这里再提一下常用的字体单位em和rem。

  ex——相当于字体中的“x”的高度。

  em——相当于字体“M”的大小,是1个方正宋体的大小,还是font-size的相对单位,因此line-height: 1.5em;就是和当前font-size相乘后的值。  

  rem——是根据html元素的font-size大小来变化,正是基于这个出发,我们可以在每一个设备下根据设备的宽度设置对应的html字号,从而实现了自适应布局

百分比的继承

  如果使用数值作为line-height的属性值,那么所有子元素继承的都是这个值;如果使用百分比相对值作为属性值,那么所有的子元素继承的是最终的计算值。

CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   body{
font-size: 14px;
line-height: 1.5;/*子元素继承的line-height都是1.5*/
line-height: 150%;/*子元素继承的line-height是14px*150%=21px*/
line-height: 1.5em;/*子元素继承的line-height14px*1.5em=21px*/
}
h3,p{
margin: 0;
}
h3{
font-size: 32px;
}
p{
font-size: 20px;
}

HTML:

1
2
   <h3>标题呵呵哈哈哈</h3>
<p>内容啊啊啊啊啊</p>

  所以一般推荐使用数值来操作line-height属性,比如1.5或15px,其中1.5默认就是1.5em,即相对于当前font-size的1.5倍。

前端配合设计师之background-position定位图片

前端配合设计师之background-position定位图片

  我们经常遇到UI设计师把同一类部件的小图片都放在一张图片里的情况,小图片就是整图分割后的各个部分,把各个部分放在一张图片上,而不是是分别存储成单独的图片,其目的就是要减少http请求次数,节省时间和带宽。对于我们前端来说,就是要把对应的小图片应用到适当的位置,这里就需要background-position属性。

  这里需要搞清楚关于定位的一些东西:

1.两个值前面一个是横向的定位,我们称为x轴方向定位。后面一个值是纵向的定位,我们称为y轴方向定位。如果只有一个值,那默认的就是x轴方向,这时y轴方向就默认的是上下居中对齐,也就是center

2.背景图片定位的坐标原点就是对应容器元素的“左上角”,即元素padding的外边缘。(这个值可以通过background-origin进行定义)

3.x y值分别表示背景图片的左上角顶点相对于坐标原点(也就是容器的左顶点)的值。

4.x y的值可以用px或百分比来表示。

5.当x y用百分比单位时,其表现与CSS中其他的百分比单位表现都不一样。

  比如一个图片:

1
img { position: absolute; left: 100%; }

  一定是在容器外部,但像background-position:100% 100%就是定位在容器内的右下角。

  那background-position百分比表示的背景图片的左上角顶点对应的容器坐标位置该如何计算呢?

  具体公式为:

positionX = (容器的宽度-图片的宽度) * percentX;
positionY = (容器的高度-图片的高度) * percentY;

  然后这样算出来的positionX和positionY就是相对于上面第三条结论的x和y,这就不难懂为什么background-position:100% 100%就是定位在容器内的右下角了吧。

6、x y也可以用“left、right、top、bottom、center”这五个关键字来表示,但注意:用“left、right、top、bottom、center”来表示的时候,“应用的是对齐规则,而不是坐标规则”。x为left是表示图片的左边和容器的左边对齐,为right的时候表示图片的右边和容器的右边对齐,y为top的时候表示图片的顶部和容器的顶部对齐,为bottom时表示图片的底部和容器的底部对齐,x y等于center的时候表示居中对齐。

7、x y用百分比或者px表示的时候,其值可以为负数。我们应用坐标规则就很容易理解负数表示的意义,x为负数时候表示图片左顶点在容器左顶点的左侧,y为负数时表示图片的左顶点在容器的左定点的上方。也就是向左和向上超出容器的范围。

但是这里要注意,有的情况下用百分比的负数时并不会像你想的那样向左和向上超出容器的范围,因为第5条中介绍的百分比定位规则,就算你
1
background-position: -50% -50%;

  图片会像background-position: 40px 10px;一样定位在容器内部,此时就可能是你图片的长度大于容器的长度导致的,因为此时:

positionX = (容器的宽度-图片的宽度) * -50% 的结果是个正值
positionY = (容器的高度-图片的高度) * -50% 的结果也是个正值

  所以就不难理解为什么会出现像background-position: 40px 10px;这样的效果了。

  详细请参考:background百分比定位值简介

JS中sort方法详解

JS中sort方法详解

  sort() 方法用于对数组的元素进行排序,并返回排序后的数组。默认排序顺序是根据字符串Unicode码点(注意这里不是按ASCII编码排序,这里按Unicode编码排序要和平常两个字符串的<,>,<=,>=,一位位字符按ASCII编码排序区分开。)

语法:arrayObject.sort(func);参数func可选,规定排序顺序,必须是函数。

  注:如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,说得更精确点,是按照Unicode字符编码的顺序进行排序。要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较。
eg:

1
2
3
4
5
var scores = [1, 10, 21, 2]; 
scores.sort();
// [1, 10, 2, 21]
// 注意10在2之前,
// 因为在 Unicode码点 指针顺序中"10""2"之前

  如果想按照其他标准进行排序,就需要提供比较函数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数 a 和 b,其返回值如下:

如果返回一个负数,则第一个参数应该排列在前面,即在排序后的数组中 a 应该出现在 b 之前。

如果 返回 0,表示a 等于 b,则a 和 b 的相对位置不变。(注意这里和排序算法的稳定性有关)

如果返回一个正数,则第二个参数应该排列在前面。

  所以,比较函数格式如下:

1
2
3
4
5
6
7
8
9
10
function compare(a, b) {
if (a < b ) { // 按某种排序标准进行比较, a 小于 b
return -1;
}
if (a > b ) {
return 1;
}
// a must be equal to b
return 0;
}

  要比较数字而非字符串,比较函数可以简单的以 a 减 b,如下的函数将会将数组升序排列:

1
2
3
function compareNumbers(a, b) {
return a - b;
}

  你可能还有疑问,就是为什么比较函数要放两个参数?我们可以拿V8引擎(Chrome浏览器的JS引擎)来理解。

  V8引擎使用C++开发,如果学过一点点数据结构课的话,肯定接触过排序这一块儿,什么快速排序、插入排序、冒泡排序的,然后大部分排序算法,步骤分解之后,最细节的操作都是两个数的相互比较,Chrome浏览器用的V8引擎就使用了快速排序。下面就贴一段当年学数据结构的时候快排的代码,V8的源码肯定不是这么简单,肯定是一种复杂的经过了各种优化(比如改善了稳定性,内存等)但基本的快排就是这样:左边右边一块儿往中间排,排出一个个有序的子段,然后再一个个子段中递归的排。

排序稳定性是指排序后数组中的相等值的相对位置没有改变,而不稳定性排序则会改变相等值的相对位置。

C++快速排序算法(这里是从小到大的正序算法):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void quick_sort(int left,int right){
if(left>right){
return;
}
int pivot=data[left],low=left,high=right;
while(low<high){
while(low<high && data[high]>=pivot){
high--;
}
data[low]=data[high];
while(low<high && data[low]<=pivot){
low++;
}
data[high]=data[low];
}
data[low]=pivot;
quick_sort(left,low-1);
quick_sort(low+1,right);
}

  所以这样就清楚了,我们只要设置好了比较函数func(a,b)规定了每两个数排序的正逆序,就可以对整个数组进行快速排序,但要注意的是,因为快速排序是不稳定排序,所以sort方法也是不稳定的

  我们还可以令对象数组排序,这里是加强版排序,首先按last排序,然后按照first排序。

JS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var s=[
{first:"Tom",last:"cruse"},
{first:"Jack",last:"awddwa"},
{first:"Aommy",last:"rththr"},
{first:"Fuck",last:"ererg"},
{first:"Shit",last:"nbnb"},
{first:"Jessy",last:"awddwa"}
];
var by=function(name,minor){
return function(o,p){
var a,b;
if(typeof o==='object' && typeof p==='object' && o && p){
a=o[name];
b=p[name];
if(a===b){
//这里的minor(o,p)实际上是by('first')(o,p)
return typeof minor === 'function' ? minor(o,p) : 0;
}
if(typeof a=== typeof b){
return a<b?-1:1;
}
return typeof a < typeof b ? -1 : 1;
}else{
throw{
name: 'Error',
message: 'Expected an object when sorting by '+name
};
}
};
};
//因为by函数使用了闭包,这里的带两个参数的比较函数实际上是by(name)函数,即func(o,p)=by(name)(o,p)
//一个函数当参数的时候只写函数名即可,所以写成sort(by('last'))就行
s.sort(by("last",by("first")));
for(var i in s){
console.log(s[i].first+" "+s[i].last);
}

  更多关于sort()的内容,请见MDN文档

label标签和任意高度元素展开收起的骚操作

label标签和任意高度元素展开收起的骚操作

   标签为 input 元素定义标注(标记)。

   元素不会向用户呈现任何特殊效果。不过,它为鼠标用户改进了可用性。如果您在 label 元素内点击文本,就会触发此控件。就是说,当用户选择该标签时,浏览器就会自动将焦点转到和标签相关的表单控件上。

   标签的 for 属性应当与相关元素的 id 属性相同。

HTML:

1
2
3
4
5
6
7
<input id="check" type="checkbox">
<p>:checked 选择器匹配每个已被选中的 input 元素(只用于单选按钮和复选框)。</p>
<div class="element">
<p>若指定了label标签的for属性和input标签的id属性相同的话,则可以实现两者的关联,在该例子里就是label也可以点击触发:checked</p>
</div>
<label for="check" class="check-in">更多↓</label>
<label for="check" class="check-out">收起↑</label>

CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*把复选框隐藏掉*/
input[type="checkbox"]{
position: absolute;
clip: rect(0,0,0,0);/*clip属性裁剪出一个绝对定位元素的可见尺寸*/
}
/*实现任意高度元素展开收起 的骚操作*/
.element {
max-height: 0;
overflow: hidden;
transition: max-height 1.25s;
}
:checked ~ .element {
max-height: 666px; /*一个足够大的最大高度值*/
}
/*为了更美观,实现两个label的显示交替*/
:checked ~ .check-in{
display: none;
}
.check-out{
display: none;
}
:checked ~ .check-out{
display: block;
}

CSS特殊的按钮闪烁效果

CSS特殊的按钮闪烁效果

  本来想练习线性渐变做按钮的,但无意中发现了一个闪烁的效果,具体见代码

HTML:

1
2
3
<div>
<a href="javascript:void(0)" class="button cyan">Daoma666</a>
</div>

CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
body>div{
text-align: center;
display: block;
width: 800px;
margin: 50px auto;
}
.button
{
font-family: Arial,helvetica,sans-serif;
font-size: 13px;
color: #000;
text-decoration: none;
display: inline-block;
text-align: center;
padding: 7px 20px 9px;
margin: .5em .5em .5em 0;/*根据相应字体大小font-size来定,在这个类里1em=13px*/
cursor: pointer;
text-shadow: 0 1px 1px rgba(0,0,0,0.4);
text-transform: capitalize;
transition: 0.1s linear;
}
.cyan {
background: rgb(130,207,241);
background: -webkit-linear-gradient(to bottom, rgba(130,207,241,1) 0%,rgba(56,174,234,1) 100%);
background: linear-gradient(to bottom, rgba(130,207,241,1) 0%,rgba(56,174,234,1) 100%);
border: 1px solid #3cafcf;
}
.cyan:hover {
background: rgb(153,216,244);
/*background: -webkit-linear-gradient(to bottom, rgba(153,216,244,1) 0%,rgba(79,183,236,1) 100%);
background: linear-gradient(to bottom, rgba(153,216,244,1) 0%,rgba(79,183,236,1) 100%);*/
//原先这里的background打错了,导致渐变无效,然后就出现了闪烁效果,感觉比较抓人眼球。
}

JS中的apply()使用详解

JS中的apply()使用详解

apply(要绑定给this的值,参数数组)方法用来调用函数及扩展作用域

eg:Function.apply(obj,args)方法能接收两个参数,其中

obj:这个对象将代替Function函数里的this对象

args:这个是数组,它将作为参数传给Function(args);  

call和apply的意思一样,只不过是参数要一个一个列出来.

eg:Function.call(obj,[param1[,param2[,…[,paramN]]]])

obj:这个对象将代替Function函数里的this对象

params:这个是一个参数列表

  1. 先看一个Function就是个函数的例子:
1
2
3
4
5
6
7
8
9
10
function Person(name,age){
this.name=name;
this.age=age;
}
function Student(name,age,grade){
Person.apply(this,arguments);
this.grade=grade;
}
var student=new Student("MaYun",21,"大三");
alert(student.name+" "+student.age+" "+student.grade);

代码解析

  这里Student里虽然没有给name和age属性赋值的语句,但调用了Person构造函数。所以var student=new Student(“MaYun”,21,”大三”);new出来的Student构造函数里:

  因为遵循构造器调用模式(new)中this被绑定到新对象(即student),所以

  Person.apply(this,arguments);就相当于Person.apply(student,arguments);就相当于student.Person(name,age,grade);

  此时Person中的this.name=name;this.age=age;中的this因为属于方法调用模式被绑定到调用该方法的对象上,所以就相当于student.name=name;student.age=age;

  1. 再来看一个Function是一个对象的方法的例子:
1
2
3
4
5
6
7
8
9
var aa={
name:"DAOMA"
};
var bb={name:"MADAO"};
bb.diff=function(){
alert(this.name+" "+arguments[0]);
}
bb.diff("啦啦啦"); //MADAO 啦啦啦
bb.diff.call(aa,"我们不一样~"); //DAOMA 我们不一样~

代码解析

  因为call的第一个参数就是用来代替前面函数里的this对象,不管Function是一个函数还是对象的方法,call的第一个参数就是替代了那个对象。

  所以bb.diff.call(aa,”我们不一样~”); 相当于 aa.diff(“我们不一样~”);

巧妙使用apply(this,args)的args参数

  如果见到apply(null,args);这种写法,这样其实不算调用什么东东了,只是可以将一个args数组默认的转换为一个参数列表(即[param1,param2,param3] 转换为 param1,param2,param3)来传惨。

比如下面的例子想得到数组中最大的一项 。

1
2
3
4
5
var array=[50,60,70,90,100];
var max=Math.max(array);
alert(max); //NaN
max=Math.max.apply(null,array);
alert(max); //100

  因为Math.max 参数里面不支持Math.max([param1,param2]) 也就是数组

  但是它支持Math.max(param1,param2,param3…),所以可以根据刚才apply的这个特点可以得到一个数组中最大的一项 。

同样的push方法没有提供push一个数组中的每一项

1
2
3
4
5
6
7
var a1=["a","b","c"];
var a2=["a","b","c"];
var b=["x","y","z"];
a1.push(b);
alert(a1.length);//4,即把b数组作为单个元素push了,a1=["a","b","c",["x","y","z"]]
Array.prototype.push.apply(a2,b);
alert(a2.length);//6,即把b数组中的每一项都push了,a2=["a","b","c","x","y","z"]

  因为push方法会修改原数组,所以这里用了a1和a2两个数组来作例子。

,