前端也能轻松年薪20w+?超详细攻略下篇来了!

还是接着我们的上文,接下来谈谈js对于新手入门需要掌握哪些东西;

  • 分清楚ECMAScript标准和W3C标准

所谓的ECMA,我们看百度百科的定义:

ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,European Computer Manufacturers Association)通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,所以它可以理解为是JavaScript的一个标准,但实际上后两者是ECMA-262标准的实现和扩展。
也就是说,ECMA标准主要是为了规范js语言,定义js语言是一种怎样的表现的一个标准;

再来看看W3C标准:

一、文档对象模型(外语缩写:DOM)根据W3C DOM规范,DOM是一种与浏览器,平台,语言的接口,使得你可以访问页面其他的标准组件。简单理解,DOM解决了Netscaped的Javascript和Microsoft的Jscript之间的冲突,给予web设计师和开发者一个标准的方法,让他们来访问他们站点中的数据、脚本和表现层对象。

这是W3C标准里面的行为标准,这是根据DOM规范,制定了一套关于web设计师如何与浏览器打交道的标准;

简单点来说,也就是ECMA标准规定了js这门语言本身应该具有的形态,例如原型、闭包、作用域等特性,我们常说的es6特性就是ECMAScript特性;

而W3C标准则是规定了js可以如何去跟DOM节点打交道,例如我们常用的window、document等js页面常见的全局对象就是标准里面定义的。

  • 了解js基本的语法;

首先js语法拥有很多的特性,例如原型链、作用域链、闭包和事件循环,还有不断加入的es6、es7新特性;

事件循环机制我之前有专门写过一篇文章来讲解是怎么一回事,有兴趣的同学可以去看看,这里附上原文链接:

简单而面试中又常见的知识点:JS执行机制

这里给小伙伴们详细讲解一下js的原型是怎么一回事,我会尽量用简短的篇幅去描述;

  • javascript原型

首先请大家先想一下什么是原型?什么是js的原型?

js中关于原型的定义其实很明确:

js的所有函数,都有一个prototype属性,这个属性本身是一个对象,对象里面又有一个constructor属性指向函数本身;

来看一下图示:

image.png

上图很显然,Person是一个函数,函数里面有一个prototype属性指向了Person Prototype,这就是函数Person的原型;原型里面总会有一个constructor属性指向Person本身;

我们接着看更进一步的图:

image.png

注意Student不是一个函数,而是一个实例对象,是通过

var Student = new Person()

实例化出来的;

我们可以看到上图的Student有一个proto属性,这就是我要讲的第二点:

每一个对象都会有一个叫做proto的属性,称为隐式原型;

它指向的是函数的prototype对象,也就是函数的原型;

那么这个属性有什么作用呢,或者说js的原型有什么作用呢?

只要记住,当一个对象的某一个属性或者方法找不到的时候,它会沿着自身的原型往上寻找,直到找到该属性或者方法为止,当然,如果找到尽头还找不到的话,会返回xx is undefined;

什么时候才是它的尽头呢?null,null是一切的终点,所以说js万物皆对象,万物皆空;

我们接着来更深入一下原型,首先我们直到一个事实:函数是对象;

我们可以通过这种方式创建函数:

var Person = new Function('console.log(1)')
Person() // 1

上面代码中可以看出,函数也是可以通过new语法来创建的,new的是什么,new的是Function这个函数;

也就是说,所有的函数,都是由Function函数实例化而来的;

那么回到我们上面所讲的,既然函数是对象,那么对象就都有proto属性指向其原型,那么Person同样也应该有proto属性,这个属性指向的是Function.prototype,所以所有函数实例的原型都是Function.prototype;

那么问题来了,我前面说过万物皆对象,那么Function.prototype自然也是一个对象,所有对象都有protp属性,所以Function.prototype是不是也有这个属性呢?

答案是肯定的,Function.prototype里面有proto属性,并且这个属性指向的是Object.prototype;

如果你听到这里觉得已经蒙了,那么恭喜你,这是很正常的现象;

为了方便小伙伴理解我刚才讲的话,特意画了一个草图把刚才的逻辑理了出来,看下方的图:

image.png

这幅图是经典原型图的简化版,可以很好的帮助我们理解js的原型关系;

从左到右分成三块:Person函数、Function函数与Object函数、Function.prototype和Object.prototype;

我们一块块来看;

  • 第一块

首先是Person函数,函数都有prototype属性,指向其原型,所以指向的是Person.prototype;

同时函数也是对象,对象都有proto属性,因为Person原型是函数,所以其隐式原型指向的是Function.prototype

  • 第二块

然后是中间的Function和Object,可以看到两个都是函数,所以都有其自身的prototype指向 自身的原型对象;

这里提一下,Function的proto属性,指向的也是Function.prototype,和prototype属性一样;

  • 第三块

然后第三块,可以看到所有的prototype对象都指向同一个地方:Object.prototype,这表示所有的原型都是对象;

然后Object.prototype总不能指向自己吧,所以js设计者将其指向了null,所以js最后是万物皆空;

最后总结一下的话,只要记住,凡是函数都有prototype属性指向函数.prototype,这就叫做该函数的原型;

原型是一个对象,所以所有原型最终指向的是Object.prototype,同时因为Object.prototype不可能再指向自身,所以它指向的是Null;

然后所有的对象都有proto属性,指向的是生成该对象的函数的prototype属性;

关于函数的原型,暂时就只讲到这里了,希望你看了之后,能对js的原型有更深的理解~

  • js的作用域与闭包

这块我会讲得更加通俗点;

js的作用域分为两个:全局作用域和函数作用域(暂时不包括es6之后的块级作用域);

作用域有内层外层的概念,也就是全局作用域是外层,定义在全局的函数是内层,定义在函数内部的函数是更内层;

  • 内层作用域可以访问到外层或者同层级的变量;
  • 但是反过来外层作用域不能访问到内层作用域的变量;

看下代码:

// 全局作用域
var a = '这里是最外层'
function foo() {
  var f = '这里是同层级'
console.log(a)
  console.log(f)
}
foo()
// 这里是最外层 
// 这里是同层级

定义在全局的foo函数内部可以访问到外部变量a和同层级的变量f;

// 全局作用域
var a = '这里是最外层'
function foo() {
  var f = '这里是同层级'
  console.log(a)
}
console.log(f)
// f is not defined

而全局作用域没办法访问到函数foo内部的变量f;

其实这也是符合我们的认知习惯的,js中还有个作用域链的概念,也就是一旦在函数内部作用域没有找到的变量,将会从内向外一层层寻找,直到找到为止;

说完了作用域,我们再来讲讲闭包;

为什么要有闭包?

我们知道外部没办法访问函数内部的变量,当我确实想要访问的时候怎么办呢?闭包就是用来干这个的,在函数内部返回另一个函数,使得外部可以访问到函数内部的变量;

来看代码:

function foo() {
  var a = 2
  return function(b) {
    return a + b
  }
}
var closureFn = foo()
console.log(closureFn(1)) // 3

函数foo返回了一个function,function里面引用了foo函数的a变量,最后成功计算得出了3;

这就是闭包最基本的使用了,至于它的原理,这里我不做太细的展开。

只提一点就是:由于返回的函数内部引用了变量a,所以foo会一直留在内存中,a变量也会一直存在内存中,直到closureFn这个引用不再使用;

闭包还有什么其他的应用场景吗?

我们经典的一个for循环大坑,代码如下:

var arr = []
for(var i = 0;i < 5;i++) {
  setTimeout(function() {
    arr.push(i)
  }, 0)
}
console.log(arr)
// [ 5, 5, 5, 5, 5]

这里输出的都是5,因为setTimeout是在js事件循环最后才执行的(有关于js事件循环机制的请参考文章前面提到的往期文章),所以最后i已经变成了5,arr.push(i)实际上等同于arr.push(5);

那么如何利用闭包解决这个事情呢?我们直接看解决代码:

var arr = []
for(var i = 0;i < 5;i++) {
  (function(i){
    setTimeout(function() {
      arr.push(i)
    }, 0)
  }(i))
}
console.log(arr) // 0, 1, 2, 3, 4

我们看到上面最大的不同,就仅仅是给setTimeout函数外层包了一层自执行函数(也就是该函数会立刻执行),并且将i作为变量传入了该函数;

为什么这样的输出反而就变正常了呢?

我们改下代码相信你就一目了然了:

var arr = []
for(var i = 0;i < 5;i++) {
  (function(any){
    setTimeout(function() {
      arr.push(any)
    }, 0)
  }(i))
}
console.log(arr) // 0, 1, 2, 3, 4

明白了吗?我们将i当前的值传给了函数,函数拿到的是值本身,并不是i这个索引;
所以即使最后i变成了5,对传给函数的参数(any)并不会有任何的影响,因为参数的值从for循环的时候就已经决定了,而不是for循环结束后再去拿当前的i的值;

以上就是作用域跟闭包的一些知识点了;

  • 了解一些常用的浏览器操作DOM节点的API

虽然有了vue、react等框架,现在使用原生js或者jq直接操作dom节点的情况已经很少了,但是我们还是要了解一些基本的api的使用;

常见的有如下api:

  • document.getElementById('id') // 根据元素id获取节点
  • document.getElementsByClassName('class') // 根据元素类名获取节点
  • document.getElementsByName('name') // 根据元素name属性获取节点
  • document.getElementsByTagName('div') // 根据元素标签名称属性获取节点
  • document.createElement('div') // 创建新的DOM节点
  • parentNode.insertBefore(新节点,目标节点) // 在父节点中,将新节点插入到目标节点之前
  • parentNode.appendChild(新节点) // 在父节点的最后插入一个新节点
  • parentNode.removeChild(子节点) // 删除父节点中指定的子节点
  • parentNode.replaceChild(新节点,老节点) // 在父节点中,用新节点替换老节点
  • ...

还有很多很多基础的api,这里不再一一列举;

那么关于javascript的必须掌握点,我们先总结到这里;

最后再提一下前端工程师必须掌握两点知识:Ajax和http协议;

  • Ajax

我们知道,ajax的出现改变了前后端的开发方式,后端能从渲染页面数据的工作中解脱出来,前端能做到更多的事情;

基本的ajax分为get请求和post请求, 我们来看下原生js实现一个ajax get请求:

var xmlHttpRequest;
function createXmlHttpRequest() {
  if(window.XMLHttpRequest) {
    // 非IE
    xmlHttpRequest = new XMLHttpRequest();
  } else if(window.ActiveObject) {
    //IE6+
    xmlHttpRequest = new ActiveObject("Msxml2.XMLHTTP");
  } else {
    // IE6-
    xmlHttpRequest = new ActiveObject("Microsoft.XMLHTTP");
  }
open('GET', url);//分别为提交的方法(GET或者POST)和提交的url
send(content);
onreadystatechange(){
  if(xmlHttpRequest.readyState == 4){
    if(xmlHttpRequest.state == 200){
      //请求成功
    }
  } else{
    //请求失败
  }
}

不过很多时候你都不需要自己封装ajax的方法,也不需要兼容ie6+的浏览器(不排除有一些传统行业还需要)

  • Http协议

最后简单讲一下http协议;

所谓http协议,指的是我们平时上网的时候,客户端用来和服务端通信最常使用的协议之一;

http协议的步骤如下:

  • 客户端连接到Web服务器
  • 发送HTTP请求
  • 服务器接受请求并返回HTTP响应
  • 释放连接TCP连接
  • 客户端浏览器解析HTML内容

Http协议是存在于应用层,和用户打交道最多的协议;

这里面知识点超级多,每一项都可以作为一篇单独的文章来发布了,因此后续可能会专门做这一块的专栏;这里的话只是简单提醒一下,小白可以根据自己的情况去搜集更多的资料来进行学习;

最后,再次感谢大家的阅读,希望这篇教程能给你一些帮助;

你可能感兴趣的:(前端也能轻松年薪20w+?超详细攻略下篇来了!)