WEB开发阿JIN

WEB开发阿JIN 查看完整档案

广州编辑  |  填写毕业院校公众号【前端研究所】  |  前端开发工程师 WEB项目总监 编辑填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

WEB开发阿JIN 发布了文章 · 9月25日

大厂面试题常考:toString与valueOf如何深入理解?结合大厂面试原题给大家做个分析

最近啊,有同学来问,数据类型转换我们都学过,可是在面试题中遇到了我们怎么就老是不会用啊。

讲到数据类型转换,我们基本上都是讲到隐式数据类型转换和显式转换。讲到数据类型转换,我们就要来聊聊,valueOf 和 toString 这两个方法了。

基本上,所有JS数据类型都拥有这两个方法,null除外。

这两个方法都是在原型链上的方法,为了处理javascript值运算与显示的问题。

valueOf 和 toString 几乎都是在出现操作符(+-*/==><)时被调用(隐式转换)。

toString

 toString返回一个表示该对象的字符串,当对象表示为文本值或以期望的字符串方式被引用时,toString方法被自动调用。

  1. 手动调用的效果

运行的效果,全部都转成了字符串。

比较特殊的地方就是,在表示对象的时候,就会打印[object Object],表示数组的时候,就打印数组内容以逗号连接的字符串,相当于数组方法Array.join(',')方法。

let a = {} 
let b = [1, 2, 3] 
let c = '123' 
let d = function(){ console.log('fn') } 
console.log(b.toString())   // '1,2,3' 
console.log(a.toString())   // '[object Object]'
console.log(c.toString())   // '123' 
console.log(d.toString())   // 'function(){ console.log('fn') }'

2. 最精准的类型判断

 toString 有时候在某种场合会比使用 typeof & instanceof 判断更高效和准确些,属于更精确的判断方式

toString.call(()=>{})       // [object Function] 
toString.call([])           // [object Array] 
toString.call('')           // [object String] 
toString.call({})           // [object Object] 
toString.call(undefined)    // [object undefined] 
toString.call(null)         // [object null] 
toString.call(22)           // [object Number] 
toString.call(new Date)     // [object Date] 
toString.call(Math)         // [object Math] 
toString.call(window)       // [object Window]

3. 什么时候会自动调用

很多同学会问了,这个toString方法,什么时候会自动调用呢?使用操作符的时候,如果其中一边为对象,则会先调用toSting方法,也就是隐式转换,然后再进行操作。

let c = [1, 2, 3] 
let d = {a:2} 
Object.prototype.toString = function(){ 
    console.log('Object') 
} 
Array.prototype.toString = function(){ 
    console.log('Array') 
    return this.join(',')   // 返回toString的默认值(下面测试) 
} 
Number.prototype.toString = function(){ 
    console.log('Number') 
} 
String.prototype.toString = function(){ 
    console.log('String') 
} 
console.log(2 + 1)  // 3 
console.log('s')    // 's' 
console.log('s'+2)  // 's2' 
console.log(c < 2)  // false        (一次 => 'Array') 
console.log(c + c)  // "1,2,31,2,3" (两次 => 'Array') 
console.log(d > d)  // false        (两次 => 'Object')

4. 重写toString方法

既然知道了有 toString 这个默认方法,那我们也可以来重写这个方法

class A {
    constructor(count) {
        this.count = count     
    }
    toString() {         
        return '我有这么多钱:' + this.count     
    } 
} 
let a = new A(100) 

console.log(a)              // A {count: 100} 
console.log(a.toString())   // 我有这么多钱:100 
console.log(a + 1)          // 我有这么多钱:1001

valueOf

valueOF这个方法,执行的时候返回当前对象的原始值。具体功能与toString大同小异,同样具有以上的自动调用和重写方法。

在下面的内容里,就不讲解其他的内容了。主要讲一下这两者的区别吧

let c = [1, 2, 3] 
let d = {a:2} 
console.log(c.valueOf())    // [1, 2, 3] 
console.log(d.valueOf())    // {a:2}

两者区别

共同点:在输出对象时会自动调用。

不同点:默认返回值不同,且存在优先级关系。

二者并存的情况下,在数值运算中,优先调用了valueOf,字符串运算中,优先调用了toString。

下面通过代码演示给大家看一下执行的原理效果

class A {
    valueOf() {
        return 2     
    }     
    toString() {
      return '哈哈哈'     
    } 
} 
let a = new A() 
console.log(String(a))  // '哈哈哈'   => (toString)
console.log(Number(a))  // 2         => (valueOf) 
console.log(a + '22')   // '222'     => (valueOf) 
console.log(a == 2)     // true      => (valueOf) 
console.log(a === 2)    // false     => (严格等于不会触发隐式转换)

执行的最后结果可以看出,如果转换为字符串时调用toString方法,如果是转换为数值时则调用valueOf方法。

但其中的 a + '22' 很不和谐,字符串合拼不是应该是调用toString方法吗?

为了验证这个问题,我们需要做一次更加严谨的演示实验看看:

暂且先把 valueOf 方法去掉

class A {
    toString() {
    return '哈哈哈'     
    } 
} 
let a = new A()
console.log(String(a))  // '哈哈哈'     => (toString) 
console.log(Number(a))  // NaN         => (toString)
console.log(a + '22')   // '哈哈哈22'   => (toString) 
console.log(a == 2)     // false       => (toString)

去掉 toString 方法看看

class A {
    valueOf() {
        return 2     
    } 
} 
let a = new A() 
console.log(String(a))  // '[object Object]'    => (toString) 
console.log(Number(a))  // 2                    => (valueOf) 
console.log(a + '22')   // '222'                => (valueOf) 
console.log(a == 2)     // true                 => (valueOf)

对比之后发现有很大不同吧。它没有像上面 toString 那样统一规整。

对于那个 [object Object],应该是从 Object 那里继承过来的,所以我们再去掉它看看

class A {
    valueOf() {
        return 2     
    } 
}
let a = new A() 
Object.prototype.toString = null;
console.log(String(a))  // 2        => (valueOf) 
console.log(Number(a))  // 2        => (valueOf) 
console.log(a + '22')   // '222'    => (valueOf) 
console.log(a == 2)     // true     => (valueOf)

总结:valueOf偏向于运算,toString偏向于显示。

  1. 在进行对象转换时,将优先调用toString方法,如若没有重写 toString,将调用 valueOf 方法;如果两个方法都没有重写,则按Object的toString输出。
  2. 在进行强转字符串类型时,将优先调用 toString 方法,强转为数字时优先调用 valueOf。
  3. 使用运算操作符的情况下,valueOf的优先级高于toString。

[Symbol.toPrimitive]

MDN:Symbol.toPrimitive 是一个内置的 Symbol 值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数。

看这个解释基本上很多同学都是看不懂的,不管你有没有学过JS。

其实不用想的太复杂,就把他看成是一个函数就可以了。

  1. 作用:同valueOf()和toString()一样,但是优先级要高于这两者;

2.该函数被调用时,会被传递一个字符串参数hint,表示当前运算的模式

一共有三种模式:

string:字符串类型

number:数字类型

default:默认

下面来看看Symbol.toPrimitive在代码中的实现吧:

class A {
        constructor(count) { 
            this.count = count     
        }
        valueOf() {
            return 2     
        } 
        toString() {
          return '哈哈哈'     
        }     
        // 我在这里     
        [Symbol.toPrimitive](hint) {
        if (hint == "number") {
            return 10;
        }        
        if (hint == "string") { 
            return "Hello Libai";        
        }        
            return true;    
        } 
} 
const a = new A(10) 
console.log(`${a}`)     // 'Hello Libai' => (hint == "string") 
console.log(String(a))  // 'Hello Libai' => (hint == "string") 
console.log(+a)         // 10            => (hint == "number") 
console.log(a * 20)     // 200           => (hint == "number") 
console.log(a / 20)     // 0.5           => (hint == "number") 
console.log(Number(a))  // 10            => (hint == "number") 
console.log(a + '22')   // 'true22'      => (hint == "default") 
console.log(a == 10)     // false        => (hint == "default")

比较特殊的是(+)拼接符,这个属于default的模式。

划重点:此方法不兼容IE,这个重点我说出来都觉得尴尬

面试题分析

讲完上面的原理比较,下面我来分享几道今年大厂的面试,题目是我学员给我提供的。

以下几道大厂必考的面试题,可以说非常完美的体现出 toString 与 valueOf 的代码作用了

1.如何让(a===1&&a===2&&a===3)的值为true?

双等号(==):会触发隐式类型转换,所以可以使用 valueOf 或者 toString 来实现。

每次判断都会触发valueOf方法,同时让value+1,才能使得下次判断成立。

class A {
    constructor(value) {
        this.value = value;     
    }     
    valueOf() { 
        return this.value++;    
    } 
} 
const a = new A(1);
if (a == 1 && a == 2 && a == 3) {
    console.log("Hi Libai!");
}

全等(===):严格等于不会进行隐式转换,这里使用 Object.defineProperty 数据劫持的方法来实现

let value = 1; 
Object.defineProperty(window, 'a', { 
get() {
    return value++     
} }) 
if (a === 1 && a === 2 && a === 3) { 
    console.log("Hi Libai!")
}

上面我们就是劫持全局window上面的a,当a每一次做判断的时候都会触发get属性获取值,并且每一次获取值都会触发一次函数实行一次自增,判断三次就自增三次,所以最后会让公式成立。

  1. 如何实现一个无限累加函数

问题:用 JS 实现一个无限累加的函数 add,示例如下:

add(1); // 1
add(1)(2);  // 3
add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10
// 以此类推 
function add(a) {
    function sum(b) { // 使用闭包
        a = b ? a + b : a; // 累加
        return sum;     
    }     
sum.toString = function() { // 只在最后一次调用        
    return a;     
}     
return sum; // 返回一个函数 
} 
add(1)              // 1
add(1)(2)           // 3
add(1)(2)(3)        // 6
add(1)(2)(3)(4)     // 10 
  1. add函数内部定义sum函数并返回,实现连续调用
  2. sum函数形成了一个闭包,每次调用进行累加值,再返回当前函数sum
  3. add()每次都会返回一个函数sum,直到最后一个没被调用,默认会触发toString方法,所以我们这里重写toString方法,并返回累计的最终值a

这样说比较好理解:

add(10): 执行函数add(10),返回了sum函数,注意这一次没有调用sum,默认执行sum.toString方法。所以输出10;

add(10)(20): 执行函数add(10),返回sum(此时a为10),再执行sum(20),此时a为30,返回sum,最后调用sum.toString()输出30。add(10)(20)...(n)依次类推。

3. 柯里化实现多参累加

这里是上面累加的升级版,实现多参数传递累加。

add(1)(3,4)(3,5)    // 16
add(2)(2)(3,5)      // 12
function add(){     
        // 1 把所有参数转换成数组 
        let args = Array.prototype.slice.call(arguments) 
        // 2 再次调用add函数,传递合并当前与之前的参数    
        let fn = function() {
            let arg_fn = Array.prototype.slice.call(arguments)         
            return add.apply(null, args.concat(arg_fn)) 
        }     
        // 3 最后默认调用,返回合并的值 
        fn.toString = function() {
            return args.reduce(function(a, b) { 
            return a + b         
        })     
        }     
            return fn 
    } 
    
    
    // ES6写法 
    function add () {
    let args = [...arguments]; 
    let fn = function(){ 
        return add.apply(null, args.concat([...arguments])) 
    }
    fn.toString = () => args.reduce((a, b) => a + b) 
    return fn; 
    }

好了,今天的内容就分享那么多,不知道对于 valueOf 和 toString方法你有没有更加深刻的理解了呢?
如果今天的分享教程你学到了东西,别忘了关注公众号【前端研究所】哦!平时也欢迎把你不懂的问题分享到公众号文章的评论留意,说不定下一次就来讲解你的疑问了呢!

查看原文

赞 10 收藏 10 评论 1

WEB开发阿JIN 发布了文章 · 6月5日

如何通过nvm安装多版本nodejs?如果nodejs安装成功,npm安装失败了怎么办?

我们在开发项目的时候,最开始,是只有一个老的项目,老项目单独安装了node版本4.4.7,后来有了新项目,由于有两个项目,但是一个需要老一些版本的node,一个需要新版本的node,因此需要在两个版本之间进行切换,这时候应该怎么做呢?

nvm可以很好的解决这个问题,我们一起来看看把。

一、什么是nvm?

   nvm就是nodejs version manage 叫做nodejs 版本管理,而nodejs有很多版本,场景如下:

   1、而你手上开发的有多个项目又分别是不同的nodejs版本,咱们就可以用nvm轻松切换!

   2、假设你正在开发的项目开始使用的nodejs版本是8.0,而现在因为某些原因,你需要升级 或者 降级 nodejs 版本,也可以使用 nvm 轻松切换

二、搭建步骤:

1、下载nvmhttps://github.com/coreybutler/nvm-windows/releases ,解压并且安装(安装nvm的地址可以自己随便选择一个位置,node地址设置为默认的:"C:\Program Files\nodejs",这里需要注意网上很多说地址不能设为"C:\Program Files",安装路径不支持空格,否则node无法使用,实际测试并无影响)

2、打开 cmd , 输入 nvm -v,如下,表示安装成功

3、(重要) 设置 node 和 npm 镜像地址

      在nvm安装目录,找到 setting.txt加上如下两行:

        node_mirror: https://npm.taobao.org/mirror...

        npm_mirror: https://npm.taobao.org/mirror...

添加后如下图所示:

4、(重要) 安装你要的nodejs版本,必须是npm和nodejs都成功,因为有时候会npm或者nodejs不会下载成功,不成功的原因很多,前提必须按我的这个步骤,并且是之前没有装nodejs,或者是卸载干净了nodejs。通过nvm list available命令查看可下载的nodejs版本信息,根据需要下载对应的版本,LTS列表示稳定版本。

nvm install 版本号

如上表示成功了,继续检验是否真的成功,进入nvm目录查看:

有红圈出来的内容就是成功的,如果没有就是不行的,就需要重新手动下载了

5、切换版本

nvm use 版本号

这样就好了,node和 npm都好了

6、nvm命令:

1,nvm nvm list 是查找本电脑上所有的node版本

- nvm list 查看已经安装的版本

- nvm list installed 查看已经安装的版本

- nvm list available 查看网络可以安装的版本

2,nvm install 安装最新版本nvm

3,nvm use <version> ## 切换使用指定的版本node

4,nvm ls 列出所有版本

5,nvm current显示当前版本

6,nvm alias <name> <version> ## 给不同的版本号添加别名

7,nvm unalias <name> ## 删除已定义的别名

8,nvm reinstall-packages <version> ## 在当前版本node环境下,重新全局安装指定版本号的npm包

9,nvm on 打开nodejs控制

10,nvm off 关闭nodejs控制

11,nvm proxy 查看设置与代理

12,nvm node_mirror [url] 设置或者查看setting.txt中的node_mirror,如果不设置的默认是 https://nodejs.org/dist/

  nvm npm_mirror [url] 设置或者查看setting.txt中的npm_mirror,如果不设置的话默认的是: https://github.com/npm/npm/ar...

13,nvm uninstall <version> 卸载制定的版本

14,nvm use [version] [arch] 切换制定的node版本和位数

15,nvm root [path] 设置和查看root路径

16,nvm version 查看当前的版本

其实安装和使用的过程并不难,但是在安装过程中,新一些的版本总是安装npm不成功,导致我一度以为是自己的安装出现问题或者环境变量和全局变量设置有问题,各种重装和设置,后来发现有人说 目前发现 8.11以上版本的node版本对应的npm都没法自动安装,需要自己到npm官网(https://npm.taobao.org/mirror... 下载手动安装对应的npm版本。下面就来讲讲遇到npm安装失败,我们应该怎么手动安装处理。

npm安装失败了怎么办??

如果你出现下面两种情况:

1.如果自己官网下载了对应的nodejs版本,但是又想用另外一个版本的npm(非nodejs自带的npm版本),

2.手动安装的nodejs成功了,但是出现下载npm安装失败的情况。

在这个时候就需要到npm的官网去下载npm的包了,那么可以用如下方法实现:

首先自己下载对应的npm(下载地址:https://npm.taobao.org/mirror...,进行配置,
或者也可以自己下载一个nodejs版本(下载地址:https://nodejs.org/download/r...),
解压后复制到nvm目录,注意命名,如:v11.11.0,这样就不需要使用 nvm install 命令安装了

(用这个方法直接安装nodejs里面就已经有自带的npm了,可以直接用,自己不用另外再下载npm了)。

然后进入npm官网( https://npm.taobao.org/mirror... )下载nodejs对应版本的npm(注意:nodejs必须有对应的npm,不知道的就按你安装nodejs的时候,提示的下载的npm版本来下载就行)。

下载完成,将解压后的文件复制到,C:\dev\nvm\v12.18.0\node_modules 目录下(一般就是 你的nvm安装目录\nodejs版本号\node_modules 这个目录),并重命名为npm(注意必须重命名为npm)

还需要将npm解压后的bin文件夹下的四个文件复制到C:\dev\nvm\v12.18.0目录下:

至此就安装成功了!

然后再用npm指令测试一下就可以了,如果出现npm不是指令或者其他报错,再出现安装就行了

前端研究所片尾2.png

查看原文

赞 3 收藏 3 评论 0

WEB开发阿JIN 发布了文章 · 3月31日

2020年前端开发应该如何准备面试(面试官角度分享)

近期疫情的影响,大家都有发现了,很多公司为了减少现金开支都在裁员,裁员的同时也有很多公司在疯狂招人。这让大家都很疑惑了,今年到底是就业容易还是就业难呢?招聘旺季金三银四还有没有?今年的就业应该如何去准备?

阿靖最近也在忙着招聘,今天就来跟大家分享今年的就业市场情况和面试关注点,帮助大家提前准备好面试。
1905236.jpg

今年的金三银四没了?

先说一下大家比较关心的金三银四,往年的3月份这个时候,是招聘量最高的时候,同比今年确实减少了一点,主要是受疫情影响,很多公司为了避免接触,加上没有全面复工的情况,招聘量比以往少了很多。

但是也有很多在疫情期间发展比较好的公司,都在非常积极的招聘,不方便接触就直接线上面试。今年的招聘情况,国家和招聘平台都在联合很多公司在做招聘宣传,预计等疫情过去,5、6月会迎来一个高峰期,到时候应届毕业生会大批涌出来,竞争也比较大。

今年的面试重点

今年的前端开发工程师面试,很多人都比较迷茫,1.担心受疫情影响,难度增加。2.Vue等框架新知识增加,会不会在面试中考到。

其实和疫情没有太大影响,今年的面试考察重在基础(技术知识和编程原理),丰富的项目(项目经验和细节),了解工作流程,面试表述到位。

其实这些面试的考察都是和往年差不多的,不过很多同学不知道怎么准备,下面我结合一下面试招聘的基本流程来讲讲如何准备,大家可以根据这几方面提前准备好。
102203.jpg

壹 第一印象—简历

简历是到公司面试的敲门砖,如果无法通过的第一步简历筛选,就没有后面的事了。

所以简历一定要写好,千万不要出现错别字或者语句不通的地方,特别是别把专业名词写错了。

如果有条件,可以找老师或者同学帮你看看简历,力求第一印象良好。

关于简历的内容怎么写,网上很多人教你如何写简历,百度一搜一大堆都不一样的,有些教你把技术栈罗列出来,有的又不推荐,很多人都看懵了。

我从面试官的角度来说,这些情况要根据个人实际情况出发来写。面试官想看到你最想表现的东西。比如技术栈的罗列,如果你简历中写的项目经验能体现你罗列的技术栈,你可以不写出来。但如果不能完全体现你的技术栈,那能写出来很有必要。

因为简历是你的能力体现,是展示你能力的地方,一定要突出自己的特长。

最后提醒:
如果是自荐简历,特别是校招,发送到邮箱的时候,一定要署名!!一般格式(谁+应聘什么岗位+技术等级),这样可以让面试官面方便找到你的简历,你也可以在众多没名字的简历中脱颖而出!

贰 技术基础知识

基础知识一直都是重点考察的内容,包含有HTML(5)、CSS(3)、JavaScript到node、webpack、Vue所有可能考察的知识。

基础知识不仅仅要知道是什么,更要明白怎么用,为什么这么用。死记硬背能应付一时,如果你遇到了想深入聊聊的面试官,死记硬背就没用了。所以每个知识点都要理解透彻,讲的清楚。

今天来罗列一些常考的面试知识点,大家可以提前准备

HTML&CSS:

flex布局、垂直居中、清除浮动、BFC、三栏布局、两栏布局、动画、盒模型、H5新特性

JavaScript:

继承、原型链、this指向、设计模式、call, apply, bind, new实现、防抖节流、let, var, const 区别、event、loop、promise使用及实现、promise并行执行和顺序执行、闭包、垃圾回收和内存泄漏、数组方法、数组乱序, 数组扁平化、事件委托、事件监听、事件模型、typescript

Vue:

vue数据双向绑定原理、vue computed原理、vue编译器结构图、生命周期、vue组件通信、mmvm模式、mvc模式理解、vue dom diff、vuex、vue-router

react:

dom-diff、列表key属性、jsx原理(createElement)、react-router原理、redux原理、生命周期、react setState、react组件通信、性能优化

网络:

HTTP1, HTTP2, HTTPS、浏览从输入网址到回车发生了什么、前端跨域、浏览器缓存、cookie, session, token, localstorage, sessionstorage、状态码、TCP连接(三次握手, 四次挥手)

叁 项目经验会考什么

面试中,项目经验非常重要,是作为能力评估的重要参考标准。对于基础知识的面试,项目经验面试更要考察你的实际开发能力,需要能讲出自己的实战理解和给出具体的实现方案。

一般都会问到一些技术上的实现技巧,可能会让你用代码来实现,一般说清楚思路就可以了,也可以写伪代码来体现。一般能表述清楚,评价不会太低。

项目经验会考察的问题包括但不限于:

项目开发流程、技术架构、说说做的比较好的项目、你在项目中担任的角色、项目中的收获、项目两点、项目中遇到的难点、简历中罗列的技术栈你是怎么理解的,如果让你运用实现你怎么做、组件设计和实现、兼容性问题、底层原理、性能优化、工程化、前端学习规划、算法

肆 和HR的面试

很多做技术的人,都很小看HR的面试环节,认为我们是做技术的,HR并不会问到技术相关的问题,感觉HR不会对我们的面试起关键作用。其实恰恰相反,HR的面试意见是占重要地位的。

在和HR聊天的时候,一定要注意技巧,有的可以说,有的不能随便说。不要以为HR和你聊天很轻松,你就什么都乱说。比如说,HR问你,是否还有面试其他公司,是否还有其他公司的offer,这个时候你不能直接回答说有多少或者完全没有。你可以说,已经面试了很多家公司,但感觉都不是很合适,想找一家能完全施展我能力的公司。要表现出在等这家公司的答复,表现出你想进这家公司。

还有一些问题,一定要快速回答,比如说绩效、上家公司的离职原因。这种问题如果你想了半天,回答得含含糊糊的,别人会认为你不诚实。这些问题最好都能提前准备好。

伍 谈薪资

谈薪资是大家最感兴趣的话题,怎么谈到一个好的薪资,才是我们去应聘的目的。这个环节也是面试中考察的重要一个环节,要看能否对自己又正确的判断。

一般我们要怎么提薪资呢?

要根据自己的实际情况,包括面试的表现和工作年限。一般跳槽的面试,在原有的情况下涨幅10%-30%是正常的,但是也不是绝对,还有50%、60%的情况,主要是要学会自我评估。如果你是初次就业,要根据自己的能力评级,结合自己的面试表现,参考市场行情来提薪资。

举个例子,如果你的的面试感觉非常好,并且你已经3年没跳槽了,可以尝试按你这个工作经验的市场价要高一点(50%、60%);如果面试表现差一点,就提30%。这个再差就再低一点。

切记薪资不能乱要!不要看别人要多少你就要多少!
每个公司都有自己的招聘预留目标,要按照自己的能力来要薪资,要太高对方公司不会和你谈,直接拒绝你的。因为投简历的不止你一个人,对方公司还有很多选择,别人性价比比你高,除非你的优势非常明显。

如果你在面试之前已经能拿到了别的公司的offer,那用来对比要高薪资是可以的。

190516.jpeg

最后总结

总结来说,面试成功=基础知识+项目经验+技术理解+表达技巧+运气。我们无法控制运气,但是我们可以在别的地方花更多时间,每个环节都提前做好准备。

面试一方面是为了找到工作,升职加薪,另一方面也是对于自我能力的考察。在我看来,能够面试成功不仅仅是来自面试前的临时抱佛脚,更重要的是在平时学习和工作中不断积累和坚持,把每个知识点、每一次项目开发、每次遇到的难点知识,做好积累,实践和总结。

前几天我有个学员问了我个问题:

现在的年轻人想成功一定要有很好的机遇,那我们是应该努力锻炼能力还是应该主动去寻找机遇呢?

在我看来,在遇到机遇之前,你要有很好的能力,抓住每一次机会积累你的能力,等到机遇来临,你不至于只能怪自己能力不足,抓不住机遇。共勉!

祝大家在疫情后都能拿到期望的offer!
更多前端开发技术分享,欢迎关注公众号【前端研究所】
前端研究所片尾2.png

查看原文

赞 21 收藏 17 评论 1

WEB开发阿JIN 发布了文章 · 3月14日

程序员跳槽该不该要高工资?面试官说出了这5点,干货满满

最近疫情基本稳定了,我们公司也开始复工了,最近我也经常做技术面试官,要面试招人,一般我在面试完技术问题后,都会照例来问一句:你的期望薪资是多少呢?

最近我很多带过的学员都来问我这个问题,很纠结,“到底该要高还是要低呢?我明明在简历上已经写了期望薪资呀”。

今天老李从面试官的角度,来直接告诉大家,如果你不敢要高薪资对你是不利的。

程序员跳槽时不敢要高工资?这可能会让你面试不成功

为什么这样说?

首先我们判断一个面试者是否能面试通过,主要是看岗位和这个面试者的能力匹配程度,如果能力非常符合要求,那肯定是没问题的。但是如果不上不下的,那就需要考虑他的优点和缺点了。

我不敢说,不敢要高工资一定会导致面试失败,但这个肯定会是个扣分点。这直接表示面试者自信不足,或者间接的暗示面试者的工作能力或者经验不行。

程序员跳槽时不敢要高工资?这可能会让你面试不成功

1 公司和项目组对岗位会有预算

其实我们项目组在开始招人的时候,要先申请一个招聘名额,提出需求,提交给公司经过审批后,会敲定这个岗位的预算,打个比方说,招一个前端开发5年经验的高级开发,除去基本的社保公积金后,会算出一个月的工资区间,比如说20-25k;如果面试者的能力特别强,完全超出了这个范围,就需要再次审批,向公司要更高的薪资需求。但如果面试者提出,每个月只要1万5的工资,这样的话多出来的钱是不会退回给项目组的。

因此,项目经理不太可能会有给公司省钱的想法,宁可花光预算,招聘到一个合适岗位的人。

比如面试者一系列面试下来面试官感觉不错,同时他的期望工资也在我们面试官的预算之内,那么基本没问题。但如果面试官觉得你能力不错,但是工资却没要足,比如只要了1万五,那么这时候项目经理反而会想,为什么要那么少呢,是不是自己感觉能力不行?还是有什么没了解到的。

这时候有一些多疑的项目经理,也会因此再多问些问题,这时候对我们面试者就不太好了。万一问到一些冷门的,发现了面试者的不足,导致无法通过面试,这就太冤。

2 多方面了解,你的岗位薪资

根据我上面说的,从公司的角度出发,能花钱绝对不省钱,所以面试者千万不要存有“因为要的工资少,老子更有竞争力”的想法。其实完全相反,很多公司不在乎每月多给几千块钱,特别是大公司。

这些公司的高管,月薪随便就是十几万甚至几十万,很多时候大家认为每个月多要几千已经是很艰难了,但对公司来说是小意思。

就从这种情况来看,如果提出的工资要求没有到达公司的预算,那真的是不要白不要了,但是要怎么能做到要工资不亏呢?

程序员跳槽时不敢要高工资?这可能会让你面试不成功

方法很简单,第一问猎头,第二看招聘信息,第三看当前行情。

其实只要前两点,就已经能了解公司的预算了。比如企业的招聘上写了,月薪15k到20k,你就不能报最低,可以要个中间值,比如要个18k,如果面试中你觉得答得很好,甚至都可以要个2k或者22k。

讲到这里,讲个废话:能力越高,期望工资越高。但这里提到的能力不仅仅是技术能力,更是面试中的面试表达技能。这中间就有很多面试技巧需要学习了,这个我在的就业班课程都会讲。

3 只要有能力,可以要薪比原公司多50%

一般跳槽,新公司会要求提供原公司的薪资证明,一般来说,新公司的工资能在原公司的基础上提升20%到50%是完全合理的,如果遇到互联网公司,甚至可以更高。

其实在决定跳槽后,在面试前,一定得准备,主要是根据职位介绍中要求的技术准备。

1 列出自己在项目里用过的技术,并结合项目说明。

2 一定得看些比较基础的难点,比如说JavaScript的闭包等等。

3 展示自己部署项目,在线分析问题的能力,这个可以结合具体案例说明。

4 尤其发现新职位所要求的技术,而自己当前项目里没用过,可以结合一些视频或培训课程来学。

程序员跳槽时不敢要高工资?这可能会让你面试不成功

总之,面试前不存在资料太少的问题,也不存在不知道如何准备的问题,只有想不想准备和准备上不上心的问题。

我经常和一些面试官沟通过,从中发现,不少面试者,入职之后工作,他们的实际工作能力高于面试时的表现。他们的工资涨幅我们是不知道的,但如果面试前准备再充分些,那是可以争取到更高工资的

4 不敢要高薪,说明不敢接受提升

当前工资低不可怕,但最怕没志气,过度追求安逸。这里倒不是推荐候选人靠吹牛等不正当的手段谋求与自己能力不匹配的工资,而是力图让大家保持一种积极向上的态势。

比如我带过不少学生,刚大学毕业的时候,月入也就5k,但经过几轮互联网公司的加持,2,3年后月入能过15k甚至更高。同时,我也见过不少在外企的朋友,每年也就涨个几百块钱,能力也就年年如此。每当周围的同事通过各种方式,收入大大提升后,他们或许也有过想法,但不久就趋于平静了。

所以从这角度来看,,面试者不敢要高工资,从而引起面试官的顾虑,这也不无道理。

程序员跳槽时不敢要高工资?这可能会让你面试不成功

5 总结,挑战高工资,首先靠的是持续的上进心

最后说一下,写这篇文章的目的不是让大家一味的在面试中不顾实际地要求高工资,更不提倡大家在面试中投机取巧,而是首先让大家保持不断上进的心态,并以此不断提升自己,在此基础上,就别在客气了,在面试的时候一定要要足工资。

搜索关注公众号-前端研究所.png

查看原文

赞 2 收藏 1 评论 1

WEB开发阿JIN 发布了文章 · 2019-08-09

8道经典JavaScript面试题解析,你真的掌握JavaScript了吗?

JavaScript是前端开发中非常重要的一门语言,浏览器是他主要运行的地方。JavaScript是一个非常有意思的语言,但是他有很多一些概念,大家经常都会忽略。比如说,原型,闭包,原型链,事件循环等等这些概念,很多JS开发人员都研究不多。

所以今天,就来和大家看看下面几个问题,大家可以先思考一下,尝试作答。

八道面试题

问题1:下面这段代码,浏览器控制台上会打印什么?
15205

问题2:如果我们使用 let 或 const 代替 var,输出是否相同
15208

问题3:“newArray”中有哪些元素?
15213

问题4:如果我们在浏览器控制台中运行'foo'函数,是否会导致堆栈溢出错误?

15217
问题5: 如果在控制台中运行以下函数,页面(选项卡) 是否会有响应
15221

问题6: 我们能否以某种方式为下面的语句使用展开运算而不导致类型错误
15224

问题7:运行以下代码片段时,控制台上会打印什么?
15228
问题8:xGetter() 会打印什么值?
15232

答案

前面的问题我们都举例出来了,接下来我们会从头到尾,一个个来分析我们这些问题的答案,给大家一些学习的思路

问题1:
使用var关键字声明的变量在JavaScript中会被提升,并在内存中开辟空间,由于没有赋值,无法定义数值类型,所以分配默认值undefined。var声明的变量,真正的数值初始化,是发生在你确定赋值的位置。同时,我们要知道,var声明的变量是函数作用域的,也就是我们需要区分局部变量和全局变量,而let和const是块作用域的。所以我们这道题的运行过程是这样的:

var a = 10; // 全局作用域,全局变量。a=10
function foo() {
// var a 
//的声明将被提升到到函数的顶部。
// 比如:var a

console.log(a); // 打印 undefined

// 实际初始化值20只发生在这里
   var a = 20; // local scope
}

图解在下面,好理解一点
15257
所以问题1的答案是:undefined

问题 2:
let和const声明可以让变量在其作用域上受限于它所在的块、语句或表达式中。和var不同的地方在于,这两个声明的变量,不会被提升。并且我们会有一个称为暂时死区(TDZ)。如果访问TDZ中的变量的话,就会报ReferenceError,因为他们的的作用域是在他们声明的位置的,不会有提升。所以必须在执行到声明的位置才能访问。

var a = 10; // 全局使用域
function foo() { // TDZ 开始

// 创建了未初始化的'a'
    console.log(a); // ReferenceError

// TDZ结束,'a'仅在此处初始化,值为20
    let a = 20;
}

图解:
15287
问题2答案:ReferenceError: a is not defined

问题3:

这个问题,是循环结构会给大家带来一种块级作用域的误区,在for的循环的头部使用var声明的变量,就是单个声明的变量绑定(单个存储空间)。在循环过程中,这个var声明的i变量是会随循环变化的。但是在循环中执行的数组push方法,最后实际上是push了i最终循环结束的3这个值。所以最后push进去的全都是3。

// 误解作用域:认为存在块级作用域
var array = [];
for (var i = 0; i < 3; i++) {
    // 三个箭头函数体中的每个'i'都指向相同的绑定,
    // 这就是为什么它们在循环结束时返回相同的值'3'。
    array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // [3, 3, 3]

图解:
15306
如果想记录每一次循环的值下来,可以使用let声明一个具有块级作用域的变量,这样为每个循环迭代创建一个新的绑定。

// 使用ES6块级作用域
var array = [];
for (let i = 0; i < 3; i++) {
    // 这一次,每个'i'指的是一个新的的绑定,并保留当前的值。
    // 因此,每个箭头函数返回一个不同的值。
    array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // [0, 1, 2]

还有解决这个问题的另外一种解决方案就是使用闭包就好了。

let array = [];
for (var i = 0; i < 3; i++) {
    array[i] = (function(x) {
     return function() {
           return x;
          };
    })(i);
}
const newArray = array.map(el => el());
console.log(newArray); // [0, 1, 2]  

问题3答案:3,3,3

问题4
JavaScript的并发模式基于我们常说的”事件循环“。
浏览器是提供运行时环境来给我们执行JS代码的。浏览器的主要组成包括有调用堆栈,事件循环,任务队列和WEB API。像什么常用的定时器setTimeout,setInterval这些全局函数就不是JavaScript的一部分,而是WEB API给我们提供的。
15335
JS调用栈是后进先出(LIFO)的。引擎每次从堆栈中取出一个函数,然后从上到下依次运行代码。每当它遇到一些异步代码,如setTimeout,它就把它交给Web API(箭头1)。因此,每当事件被触发时,callback 都会被发送到任务队列(箭头2)。
事件循环(Event loop)不断地监视任务队列(Task Queue),并按它们排队的顺序一次处理一个回调。每当调用堆栈(call stack)为空时,Event loop获取回调并将其放入堆栈(stack )(箭头3)中进行处理。请记住,如果调用堆栈不是空的,则事件循环不会将任何回调推入堆栈。

好了,现在有了前面这些知识,我们可以看一下这道题的讲解过程:
实现步骤:

  1. 调用 foo()会将foo函数放入调用堆栈(call stack)。
  2. 在处理内部代码时,JS引擎遇到setTimeout。
  3. 然后将foo回调函数传递给WebAPIs(箭头1)并从函数返回,调用堆栈再次为空
  4. 计时器被设置为0,因此foo将被发送到任务队列(箭头2)。
  5. 由于调用堆栈是空的,事件循环将选择foo回调并将其推入调用堆栈进行处理。
  6. 进程再次重复,堆栈不会溢出。

问题4答案:堆栈不会溢出。

问题5:
在很多时候,很多做前端开发的同学都是认为循环事件图中就只会有一个任务列表。但事实上不是这样的,我们是可以有多个任务列表的。由浏览器选择其中一个队列并在该队列进行处理回调。
从底层来看,JavaScript中是可以有宏认为和微任务的,比如说setTimeout回调是宏任务,而Promise回调是微任务。

他们有什么区别呢?
主要的区别在于他们的执行方式。宏任务在单个循环周期中一次一个低堆入堆栈,但是微任务队列总是在执行后返回到事件之前清空。所以,如果你以处理条目的速度向这个队列添加条目,那么你就永远在处理微任务。只有当微任务队列为空时,事件循环才会重新渲染页面。

然后我们再回到我们前面讲的问题5中:

function foo() {
  return Promise.resolve().then(foo);
};    

我们这段代码,每次我们去调用【foo】的时候,都会在微任务队列上加另一个【foo】的回调,因此事件循环没办法继续去处理其他的事件了(比如说滚动,点击事件等等),直到该队列完全清空位置。因此,不会执行渲染,会被阻止。

问题5答案:不会响应。

问题6:
在我们做面试题的时候,展开语法和for-of语句去遍历iterable对象定义要遍历的数据。其中我们要使用迭代器的时候,Array和Map都是有默认迭代操作的内置迭代器的。
但是,对象是不可迭代的,也就是我们这道题里的,这是一个对象的集合。但是我们可以使用iterable和iterator协议来把它变成可以迭代的。
在我们研究对象的时候,如果一个对象他实现了@@iterator方法,那么它就是可以迭代的。这意味着这个对象(在他的原型链上的一个对象)必须是又@@iterator键的属性的,然后我们就可以利用这个键,通过常量Symbol.iterator获得。
下面是这道题的举例写法:

var obj = { x: 1, y: 2, z: 3 };
obj[Symbol.iterator] = function() {
    // iterator 是一个具有 next 方法的对象,
    // 它的返回至少有一个对象
    // 两个属性:value&done。
    // 返回一个 iterator 对象
    return {
        next: function() {
            if (this._countDown === 3) {
               const lastValue = this._countDown;
               return { value: this._countDown, done: true };
              }
            this._countDown = this._countDown + 1;
            return { value: this._countDown, done: false };
        },
        _countDown: 0
    };
};
[...obj]; // 打印 [1, 2, 3]

问题6答案:如上是一种方案,可以避免TypeError异常。

问题7:
在看这个问题的时候,我们要先理解for-in循环遍历本身的可枚举属性和对象从原来的原型继承来的属性。可枚举属性是可以在for-in循环期间可以访问的属性。
当我们知道这个知识点前提了之后,我们在看这道题,你就知道这道题打印的其实就是只能打印这些特定的属性。

var obj = { a: 1, b: 2 }; //a,b 都是可枚举属性

// 将{c:3}设置为'obj'的原型,
// 并且我们知道for-in 循环也迭代 obj 继承的属性
// 从它的原型,'c'也可以被访问。
Object.setPrototypeOf(obj, { c: 3 });

// 我们在'obj'中定义了另外一个属性'd',
// 但是将'enumerable'可枚举设置为false。 这意味着'd'将被忽略。
Object.defineProperty(obj, "d", { value: 4, enumerable: false });
//所以最后使用for-in遍历这个对象集合,那就是只能遍历出可枚举属性
for (let prop in obj) {
    console.log(prop);
}

// 也就是只能打印
// a
// b
// c

图解
15436
问题7答案:a、b、c

问题8:
首先我们可以看到var x是一个全局遍历,在不是严格模式下,这个X就直接是window对象的属性了。在这段代码里,我们最重要是要理解this的对象指向问题,this始终是指向调用方法的对象的。所以,在foo,xGetter()的情况下,this指向的是foo对象,返回的就是在foo中的属性x,值就是90。但是在xGetter()的情况下,他是直接调用的foo的getx()方法,但是其中this的指向是在xGetter的作用域,就是指向的window对象中,这时指向的就是全局变量x了,值也就是10。

var x = 10; // 全局变量
var foo = {
    x: 90,//foo对象的内部属性
    getX: function() {
         return this.x;
    }
};
foo.getX(); // 此时是指向的foo对象,
//所以打印的是X属性 值就是90
let xGetter = foo.getX;//xGetter是在全局作用域,
//这里的this就是指向window对象
xGetter(); // 打印 10

15466
问题8答案:10

最后

ok,我们的8道问题都解决了,如果你前面写的答案全部都正确,那么你非常棒!去面试前端工作起码12k起步了。就算做不出来或者做错了也没有关系,我们都是不断通过犯错来学习的,一步步的理解错误,理解背后的原因,才能进步。

更多技术好文,前端开发学习教程,欢迎关注公众号【前端研究所】看更多前端技术文章!
图片描述

查看原文

赞 148 收藏 117 评论 10

WEB开发阿JIN 发布了文章 · 2019-05-24

前端开发工程师,找工作应该选择大公司还是小公司?

大公司做开发有面子,小公司做开发有金子?
小公司做开发是否更容易拿到offer?
大公司做开发是否会接触到最前沿的项目?
大小公司做开发,职业发展前景如何?
金三银四虽然过去了,但现在来谈这些问题还不迟,如果你准备在下半年就业,那今天教你如何选择就业公司,帮助自己打好职业发展的黄金五年第一枪!

其实上面这些问题都是我在这个行业那么多年,很多学员或者朋友经常问我的,那么我今天就来给大家讲一下这些问题,分析一下大小公司的优劣,以及大家在就业选择的时候需要注意的一些地方。

薪资福利比较

待遇和福利,这个是大家最关心的,先来聊最刺激的。

大公司
国内的大公司,肯定就是选择一二线的互联网公司了。
第一梯队:BAT(百度,阿里,腾讯),开发工程师心之所向,技术天堂。
第二梯队:网易,美团,字节跳动,滴滴,360,京东,爱奇艺,小米,携程等等这些发展不错的互联网公司
13645

一线公司的特点就是钱多,公司运行稳定,福利待遇充足,比如说腾讯的底薪比同行的高出30%,员工餐厅、咖啡厅,健身房,K歌跳舞打游戏,住房福利等等。还有阿里的也是高薪,住房+教育福利,特色食堂,各种娱乐活动。百度offer直接就是14.6个月薪水,小吃点心送到工位,员工宿舍,健康保障。
13652
13651

小公司
但是小公司的福利你以为就会很差吗?
国内很多小公司福利都还是不错的,招起开发人员来也是从不“手软”,同水平技术能力,基本的开发工资待遇底薪都会和BAT相当 。
虽然说整体的福利不能完全和打公司媲美,但是也是能别出心裁,努力表现出来对员工的关怀的。比如说餐补等各种补助,健身房,员工饭堂咖啡厅,节假日问候礼等等。
13655

如果你运气好,找对了公司,正好是在小公司蓬勃发展的时期加入了公司,到上市的时候,你还能以创始员工身份获得丰厚的股份收益。
这就是很多人做开发的人说的:
选对startup,一夜致富不是梦。
13589

学习机会比较

大公司
在大公司做开发,你能学习到规范化的工作流程和职业技能的系统性培养。
这可以帮助你培养出高效良好的工作习惯。而一旦掌握了正确的工作习惯,以后无论做什么岗位都能快速上手。
打下这样良好的基础,在开发工程师黄金5年里是非常重要的。
13640
此外,大公司的员工可获得更多的知识和学习资源。
比如,不同于小公司,大公司一般都有非常完善的代码文档和 CodeBase。千万不要小看好像流水账一样的记录内容,这可都是宝贵的学习资源。比如阿里就有专门的java开发规范手册。
通过查看这些文档,开发工程师可以从中了解到某个专业的复杂的企业级项目,以及它每一部分的设计思路、每一段代码的具体作用。
又比如,很多大公司都会有内部培训课程,manager会根据不同员工的工作需求,建议他们去上不同的课程,汲取新的技术知识。

小公司
在学习机会上,小公司就没办法有大公司那么多的积累了,但是小公司最大的优势就没有边界的“野蛮生长”。
13643
在小公司,你会接触到各种各样的任务,获得来自不同岗位、不同业务的工作经验和锻炼机会,直接可以锻炼你的能力广度。
不像大公司里,一年到头见不到大老板几面,在国内很多小公司都是扁平化管理,所以你可以在小公司随时随地与“高层”零距离接触。
这就意味着,你有更多的机会和“大牛”一起工作,并从中获得更多高级的工作经验。而这样的机会,很多人要在大公司待4、5年才有可能获得。

职业发展比较

作为一名开发工程师,你所参与开发企业项目的影响力和参与度都是很重要的,这个是可以直接影响你的职业发展的。
大公司
在大公司,由于公司规模大,厉害的人也多,你的影响力和参与度十分有限。
而且,因为公司体系完整,分工明确,你很有可能会日复一日的做相同的工作,有种“螺丝钉”的感觉。
有时候,即使你有很好的想法,可是因为大公司有很长的决策流程和复杂的人事制度,所以等你的想法层层上报,时机可能已经过去了。
13692
普遍的说,刚进大公司的新人,起码要花3个月的时间才能熟悉公司的流程,适应规则。一点点来,慢慢的才能产生实际的参与度和影响力。

不过也有好处,大公司的企业项目远超小公司的庞大项目,在未来,如果你有机会参与到这些复杂的问题,也能接触到有社会影响力的大项目,大公司会作为你升级个人能力很好的一个平台。
从这个角度来看,大概是的长期发展上限会比小公司高一些。
13724

但是,并不是所有的“螺丝钉”都能熬出头,想要达到这样一个上限,不断地学习和长期的个人努力是必不可少的。
因此,很多开发工程师在大公司待了一段时间后,就会跳槽去小公司寻找突破点。
小公司
在小公司里,由于公司规模不大,很多时候需要一个人做更多的事情,参与的事情多了,一开始的个人影响力和上升空间会更大一些。
举个例子,小公司没有像大公司那样,有非常完整的代码文档,做开发工程师,不仅要学会"拧螺丝",还要学会自己摸索着“造轮子”、“造发动机”,最后“造一台完整的车上”。
虽然自己造出来的车子没有大公司造的跑得快,但是你还是可以获得满满的参与感和成就感。
不过在小公司工作,就没有在大公司工作那样的“安全感”了。毕竟公司的发展和个人的发展是密不可分的。
如果一开始就选好队伍,遇到像瑞幸咖啡这样的潜力股,当然前途无限光明啦
但是也有很多创业公司在初期摸爬滚打,好不容上市了却股票暴跌,很多员工不得不跳槽的也有。

招聘标准比较

大公司
对大公司来说,他们有比较深厚的技术和财务基础,更有能力培养新人。
因此,在面试的时候,大公司对应聘者的实战经验不一定要求很高,通常会主要考察应聘者的技术知识体系,学习能力和基本素养。
13987
虽然对经验要求比较低,但是在学历方面,也会趋向学习能力更强的高学历。

小公司
而小公司,比如说像大疆、瓜子、小红书这些独角兽公司的面试要求反而更高,有时候甚至比BAT还严格。
除了基本功之外,他们也非常看重应聘者是否有和公司现有业务的项目的实际工作经验,是否能快速的融入技术团队。
13991

这主要是因为小公司发展还不成熟,产品需要快速迭代,而团队规模比较小,他们需要的是能入职后快速做事的老手。

最后

其实大公司和小公司各有优劣,没有好不好,只有合不合适。
但是在大家选择的时候,可以将以下几点作为重点考虑:

  • 自己的职业目标 + 方向
  • 公司未来的成长形势

不过,无论你去到哪个公司,扎实的基本功和丰富的项目经历,才是打开你求职道路的不二法门。

图片描述

查看原文

赞 2 收藏 2 评论 0

WEB开发阿JIN 回答了问题 · 2019-03-06

面试官问,怎么理解原型原型链,我该怎么答?

ECMAScript中只支持实现继承,而且是通过原型链的方式来实现的。所以原型链是JavaScript实现继承的一种重要方式。
1.每个函数对象都有一个 prototype 属性,这个属性就是函数的原型对象。
2.原型链是JavaScript实现继承的重要方式,原型链的形成是真正是靠__proto__ 而非prototype。
推荐看看这篇文章:
如何理解JavaScript的原型和原型链?

关注 9 回答 7

WEB开发阿JIN 发布了文章 · 2019-02-19

面试官问我:什么是JavaScript闭包,我该如何回答

闭包,有人说它是一种设计理念,有人说所有的函数都是闭包。到底什么是闭包?这个问题在面试是时候经常都会被问,很多小白一听就懵逼了,不知道如何回答好。
这个问题也有很多朋友在公众号给李老师留言了,问题表达方式不一样,都是终归到一点,就是对闭包没有很清晰的理解。大家经常去网上找相关资料,但是对闭包的说法都是各种各样的,让大家对闭包的定义没有一个概念。
所以今天我们来一起讲讲什么是闭包,帮助大家理解,今天的内容可以直接收藏起来。方便以后看。

什么是闭包(Closure)

简单讲,闭包就是指有权访问另一个函数作用域中的变量的函数。
MDN 上面这么说:闭包是一种特殊的对象。
它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。

这种官方的概念是比较难理解的,在面试的时候说出来也不是很专业,因为没办法有个具体的逻辑。
我个人认为,理解闭包的关键在于:外部函数调用之后其变量对象本应该被销毁,但闭包的存在使我们仍然可以访问外部函数的变量对象,这就是闭包的重要概念。
10692

产生一个闭包

创建闭包最常见方式,就是在一个函数内部创建另一个函数。下面例子中的 closure 就是一个闭包:
10698
闭包的作用域链包含着它自己的作用域,以及包含它的函数的作用域和全局作用域。

闭包的注意事项

.通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,在创建了一个闭包以后,这个函数的作用域就会一直保存到闭包不存在为止。
10747
从上述代码可以看到add5 和 add10 都是闭包。它们共享相同的函数定义,但是保存了不同的环境。在 add5 的环境中,x 为 5。而在 add10 中,x 则为 10。最后通过 null 释放了 add5 和 add10 对闭包的引用。
在javascript中,如果一个对象不再被引用,那么这个对象就会被垃圾回收机制回收;
如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。

闭包只能取得包含函数中任何变量的最后一个值
10758
大家看一下上面这个代码,arr数组中包含了10个匿名函数,每个匿名函数都能访问外部函数的变量i,那么i是多少呢?
当arrFunc执行完毕后,其作用域被销毁,但它的变量对象仍保存在内存中,得以被匿名访问,这时i的值为10。
要想保存在循环过程中每一个i的值,需要在匿名函数外部再套用一个匿名函数,在这个匿名函数中定义另一个变量并且立即执行来保存i的值。
10763
这时最内部的匿名函数访问的是num的值,所以数组中10个匿名函数的返回值就是1-10。

闭包中的this对象

10772
在上面这段代码中,obj.getName()()实际上是在全局作用域中调用了匿名函数,this指向了window。
这里要理解函数名与函数功能是分割开的,不要认为函数在哪里,其内部的this就指向哪里。
window才是匿名函数功能执行的环境。
如果想使this指向外部函数的执行环境,可以这样改写:
10779
在闭包中,arguments与this也有相同的问题。下面的情况也要注意:
10782
obj.getName();这时getName()是在对象obj的环境中执行的,所以this指向obj。
(obj.getName = obj.getName)赋值语句返回的是等号右边的值,在全局作用域中返回,所以(obj.getName = obj.getName)();的this指向全局。要把函数名和函数功能分割开来。

内存泄漏

闭包会引用包含函数的整个变量对象,如果闭包的作用域链中保存着一个HTML元素,那么就意味着该元素无法被销毁。所以我们有必要在对这个元素操作完之后主动销毁。
10791

函数内部的定时器

当函数内部的定时器引用了外部函数的变量对象时,该变量对象不会被销毁。
10795

闭包的应用

应用闭包的主要场合是:设计私有的方法和变量。
任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量。私有变量包括函数的参数、局部变量和函数内定义的其他函数。
把有权访问私有变量的公有方法称为特权方法(privileged method)。
10803
在这里,我们需要理解两个概念:
模块模式(The Module Pattern):为单例创建私有变量和方法。
单例(singleton):指的是只有一个实例的对象。JavaScript 一般以对象字面量的方式来创建一个单例对象。
10809
上面是普通模式创建的单例,下面使用模块模式创建单例:
10813
匿名函数最大的用途是创建闭包,并且还可以构建命名空间,以减少全局变量的使用。从而使用闭包模块化代码,减少全局变量的污染。
10817
在这段代码中函数 addEvent 和 removeEvent 都是局部变量,但我们可以通过全局变量 objEvent 使用它,这就大大减少了全局变量的使用,增强了网页的安全性。

运用闭包的关键

闭包引用外部函数变量对象中的值;
在外部函数的外部调用闭包。
闭包的缺陷
闭包的缺点就是常驻内存会增大内存使用量,并且使用不当很容易造成内存泄露。
如果不是因为某些特殊任务而需要闭包,在没有必要的情况下,在其它函数中创建函数是不明智的,因为闭包对脚本性能具有负面影响,包括处理速度和内存消耗。
最后 来一道有关闭包的面试题
下面代码中,标记 ? 的地方输出分别是什么?
10829
大家结合今天讲解的内容,思考一下答案,大家可以把答案发到留意上吧。

好了,今天的讲解就那么多,如果你还有什么前端问题想提问的,或者你想李老师下次给大家讲什么内容,可以直接留意提问,说不定下次文章就会讲解了。
如果你觉得这篇文章对你有帮助,请转发点赞支持一下!

查看原文

赞 28 收藏 23 评论 1

WEB开发阿JIN 发布了文章 · 2019-02-16

2019年前端学习路线

近两年来,前端开发工程师越来越火了,
2019年已经到来了,很多准备入行前端开发工程师的小伙伴们,不知道准备得怎么样了呢?有的朋友在想方设法的学习,争取在年后的金三银四能靠实力找到一份满意的工作!还有的小伙伴很迷茫,想学前端,却没有方向!


今天来给大家讲讲,在2019年,我们学习前端开发,如何才能高效学会前端开发?

零基础起步

首先,无论学任何一个技术,都是从零基础开始的,前端开发也是一样。做软件开发,是从事编程开发工作,必须先从语法基础开始学习,通过语法组成产品效果。
前端开发的基础语法,由HTML+CSS+JavaScript组成,这是前端开发最基本的3个语言。
网页布局基础:HTML+CSS
HTML就是超文本标记语言,组成网页内容的最基本语言。你可以直接说他是网页的骨架,网页的图片、文字、视频、音频、程序都需要他引入到网页中体现。
图片描述
光是HTML做网页,只是有了内容,当然是远远不够的。因为只是HTML的话,只能用Table做布局才能勉强做出个成型的网页来。但是从Web2.0时代开始,都是盒子模型布局法了,也就是用DIV+CSS来实现布局了。
CSS就是层叠样式表。通过样式属性来对标签进行布局规范,在不再使用table布局的时候,只要CSS样式对网页标签进行对应的布局实现才是正确的开发方式了。
图片描述
HTML(div)+CSS布局,是基础入门的基本步骤,在这个阶段,你需要学习的内容,包括有:
1.标签语义化,SEO
2.页面加载的流程和原理
3.网页结构
4.盒子模型(W3C盒子模型和IE盒子模型)
5.CSS选择器
6.CSS布局浮动、定位

在刚刚开始的阶段,大家学习的布局方式基本都是以px为单位的静态布局方法。熟悉好布局方法,先给自己一个小目标,做一个简单的电商网页的基本结构出来,不用特效,不需要交互。

浏览器脚本语言:JavaScript
JavaScript是我们学习前端开发中非常重要的一个内容,也是一个大家经常掉坑里的难点。JavaScript现在可以说是互联网时代使用率最高的脚本语言了,在网页中,所有的数据渲染,特效的交互都需要利用JavaScript,来影响浏览器的显示。
图片描述
JavaScript不只是开发网页特效和渲染数据的重要内容,在学习前端开发的后期,大量使用算法和框架的时候,对JavaScript基础的考验也是很多的。
在基础阶段,我们学习JavaScript需要注意:
1.基本关键字指令
2.基本数据类型、数组
3.函数
4.面向对象编程
5.原型链、闭包
6.JSON
7.Ajax
8.DOM(文档对象模型 原生DOM操作)
9.事件捕获、冒泡、代理
10.常用函数方法
11.ES5、6、7

在JavaScript部分学习,主要还是要去理解好交互的原理,把原理分析清楚,真的理解语法,那写出多复杂的逻辑也是手到擒来。

JavaScript经典类库jQuery
说到学习JavaScript,很多小白同学肯定会很头痛他的原生写法。每次逻辑业务都需要手动写,也就是用一次就造一次轮子。觉得很麻烦。如果能简单一点就好了。
因为JavaScript有可以封装的特性,所以在后面也出现了很多用JavaScript封装的类库、插件。比如说最经典的类库就是jQuery了。
jQuery类库就是在类库里面封装好了很多JavaScript的事件方法。jQuery通过封装,减轻了遍历、对象选择等等很多的问题。把网页特效的实现变得简单化,通过调用方法就可以了。
图片描述
在学习jQuery的时候,需要重点认识的有:
1.jQuery语法和JavaScript原生语法的差异
2.Dom对象和jQuery对象
3.jQuery的入口函数和JavaScript的入口函数的差异
4.jQuery事件的执行逻辑。
学习jQuery,最需要达到的效果就是能快速的完成网页的特效,比如说轮播图、手风琴菜单、旋转木马、放大镜等等这些特效。能完成一个电商网站的布局+特效开发是最基本的要求了。

这些就是零基础学习必备的一些基本内容,在入门前端开发最基本需要掌握的东西,把基础理解好,才能为后面的学习做更多的准备。

多终端进阶学习

除了基础的PC端,目前移动端可以说是非常火的了。
比PC端单一的网页不同,在移动端的技术可以应用到WEB-APP,小程序,Hybrid-App等等。
Web-App
也就是我们常见的浏览器(以及内置浏览器,比如微信)打开的大型移动端网页。比如我们常见的电商网站,功能性网站,管理网站,在布局和功能上都有APP的效果。
做好Web-App开发,最基本的很多人肯定会说响应式布局,但是前端开发是又5种布局法的,除了出名的响应式,还有在移动端最重要的弹性布局法,也就是很多人头疼的rem布局。
图片描述
除了布局方法之外,在H5新特性和触屏事件和设备兼容性问题也是需要信手拈来。
小程序
这个也不用多说,现在可以说是非常火的,各大平台都有在做自己的小程序,各种砍价,抢票,电商,游戏都有运用。这个以微信小程序为例,主要是微信团队基于前端基础来做的封装语法,主要的还是ES语法。小程序目前很多公司都是招聘前端开发,目前还没有独立的小程序开发工程师,所以小程序可以说是前端工程师高薪就业的加分技能,换句话说就是成熟开发必备了。
图片描述
Hybrid-App
又称混合式APP,可能听说的人很少,但是18年是越来越多人去开发了,这种是能直接产出下载到终端的APP的,在体验感上可以说已经具备传统APP的大部分功能了。这种开发门槛较低,也就是前端开发就可以完成。多数都是大包平台就能做,这个你可以后面了解一下。
图片描述

前端主流技术框架

前面的都是基础东西,现在去就业前端开发,不是只靠个基础东西,搞个移动端页面就可以算成熟了。前端3大框架,VUE、Angular、React这3个可以说是现在非常火热的了。
图片描述
基础语法都可以写的前端,为什么还要框架?
很多小白朋友是不懂什么是框架的,只听过jQuery这些东西,以为就是框架。或者认为框架就是加速开发,觉得这些库、插件就能完成框架的工作了。
其实框架的出现,是改变前端地位的重要标志。最重要的表现,就是前后端分离,在前后端分离之前,很多后端开发都是又当爹又当妈的,效果不好效率也不高,我就是在后端出身,深知痛苦。
现在的前端项目,比以前是更加复杂化、多样化了。项目复杂了,问题也多了。
那框架到底解决了什么问题?
解决重复引用外部js,以用jQuery开发为例,很多时候都是不能单一完成一个项目的,还需要引用很多的第三方插件和库,导致会一个项目引入很多外部JS文件。
图片描述
这样不仅让代码变得杂乱,而且很影响打开速度。但是用框架呢,以VUE为例,一般会和构建工具配合,然后就是一个入口文件就可以完成了,在运行时候就在入口引入一次,一劳永逸。
使用组件化开发,组件是前端框架里非常强大的功能之一,它可以扩展你的HTML,封装可以重用的代码块,比如你的轮播图、tab切换、页面头部、页面底部等等。
这种独立的组件具有了结构(html),表现(css)和行为(js)完整的功能,很大程度的节省了代码量,提高了代码的复用性。特别是团队合作的时候,可以很好的提高使用效率。
减少开发周期,如果你觉得jQuery可以减少开发周期了,那其实框架可以比库更快。比如说使用jQuery开发的时候,很多时候是需要频繁去操作DOM,每次效果都要去查找DOM,这样就显得很繁琐了。使用框架的时候,很多功能都得到了封装,比如说很多指令都有数据绑定,数据格式化这些功能。这样更多时候,我们开发的时候只需要关注数据的逻辑就行了。

最后
这些也就是我们学习前端开发必备的一些知识点了。学习路线放一下给大家。
12051
前端开发学习不是单一的,内容比较多,同样应用的场景也非常多。如果你想从事前端开发工作,就要更急专心和努力,坚持方向不动摇!
更多前端开发学习,关注公众号【前端研究所】每天更新更多学习教程和方法。

查看原文

赞 11 收藏 7 评论 1

WEB开发阿JIN 发布了文章 · 2019-01-09

2019年如何成为一个成熟的前端开发者?

2019年一眨眼就到了,很多想入行前端开发的小白朋友们,不知道在新的一年实现了没有呢?

最近有个小伙伴来问,问我怎么样才能成为一名成熟的前端开发?

640?wx_fmt=png

是的,成熟的专业开发工程师现在是越来越吃香了,零基础小白就业越来越难了。

如何成为一名成熟前端开发?

首先,成为一名成熟的开发者,首先必是一位有扎实的基础开发。

所以最基本的一些HTML、CSS、JavaScript这些基础的语法,就不详细说了。最基本的网页布局还是要简单掌握的。除了基础的语法,还有很多需要注重去理解的地方。

扎实的JavaScript基础

在前端开发里,最讲究的还是在JavaScript里,基础的语法,能运用很简单。

640?wx_fmt=jpeg

但是说成熟开发,那必然是对JavaScript是熟练掌握和有深入的理解了。说到要熟练掌握JavaScript,那必然是要掌握闭包,ES678,原型链,这一系列的了。

除了扎实的JS基础,还有就是要掌握符合目前市场需求的技术点了。

多终端开发

除了基础的PC端,目前移动端可以说是非常火的了。

比PC端单一的网页不同,在移动端的技术可以应用到WEB-APP,小程序,Hybrid-App等等。

640?wx_fmt=jpeg

WEBapp,也就是我们常见的浏览器(以及内置浏览器,比如微信)打开的大型移动端网页。比如我们常见的电商网站,功能性网站,管理网站,营销网页等等,在布局和功能上都有APP的效果。

640?wx_fmt=png

做好WEB-APP开发,最基本的很多人肯定会说响应式布局,但是前端开发是又5种布局法的,除了出名的响应式,还有在移动端最重要的弹性布局法,也就是很多人头疼的rem布局。

除了布局方法之外,在H5新特性和触屏事件和设备兼容性问题也是需要信手拈来。

小程序,这个也不用多说,现在可以说是非常火的,各大平台都有在做自己的小程序,各种砍价,抢票,电商,游戏都有运用。这个以微信小程序为例,主要是微信团队基于前端基础来做的封装语法,主要的还是ES语法。

640?wx_fmt=jpeg
小程序目前很多公司都是招聘前端开发,目前还没有独立的小程序开发工程师,所以小程序可以说是前端工程师高薪就业的加分技能,换句话说就是成熟开发必备了。

Hybrid-App,又称混合式APP,可能听说的人很少,但是18年是越来越多人去开发了,这种是能直接产出下载到终端的APP的,在体验感上可以说已经具备传统APP的大部分功能了。

640?wx_fmt=jpeg

这种开发门槛较低,也就是前端开发就可以完成。多数都是大包平台就能做,有很好的前端基础就可以做了。

前端主流技术框架

前面的都是基础东西,现在去就业前端开发,不是只靠个基础东西,搞个移动端页面就可以算成熟了。前端3大框架,VUE、Angular、React这3个可以说是现在非常火热的了。

640?wx_fmt=jpeg

基础语法都可以写的前端,为什么还要框架?
很多小白朋友是不懂什么是框架的,只听过jQuery这些东西,以为就是框架。或者认为框架就是加速开发,觉得这些库、插件就能完成框架的工作了。

其实框架的出现,是改变前端地位的重要标志。最重要的表现,就是前后端分离,在前后端分离之前,很多后端开发都是又当爹又当妈的,效果不好效率也不高,我就是在后端出身,深知痛苦。

现在的前端项目,比以前是更加复杂化、多样化了。项目复杂了,问题也多了。

那框架到底解决了什么问题?
解决重复引用外部js,以用jQuery开发为例,很多时候都是不能单一完成一个项目的,还需要引用很多的第三方插件和库,导致会一个项目引入很多外部JS文件。

640?wx_fmt=png

这样不仅让代码变得杂乱,而且很影响打开速度。但是用框架呢,以VUE为例,一般会和构建工具配合,然后就是一个入口文件就可以完成了,在运行时候就在入口引入一次,一劳永逸。

使用组件化开发,组件是前端框架里非常强大的功能之一,它可以扩展你的HTML,封装可以重用的代码块,比如你的轮播图、tab切换、页面头部、页面底部等等。

640?wx_fmt=jpeg

这种独立的组件具有了结构(html),表现(css)和行为(js)完整的功能,很大程度的节省了代码量,提高了代码的复用性。特别是团队合作的时候,可以很好的提高使用效率。

减少开发周期,如果你觉得jQuery可以减少开发周期了,那其实框架可以比库更快。比如说使用jQuery开发的时候,很多时候是需要频繁去操作DOM,每次效果都要去查找DOM,这样就显得很繁琐了。

640?wx_fmt=jpeg

使用框架的时候,很多功能都得到了封装,比如说很多指令都有数据绑定,数据格式化这些功能。这样更多时候,我们开发的时候只需要关注数据的逻辑就行了。

没有真正的成熟

你会提出成熟,是因为想给自己一个标准,但是做技术工作,最重要还是市场的标准。

前端技术是日新月异的,基本每年都是会有新的概念,新的架构,新的应用产品,新的交互体检。这些都是有不确定性的。

640?wx_fmt=jpeg

做技术没有真正的成熟可言,更多的还是不断学习,持续进步。

把技术不断做到专,做到精,才能在当前时代成为“成熟”。


送书啦!免费送书!马上扫码来参加吧!

图片描述

查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 231 次点赞
  • 获得 0 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 0 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-02-25
个人主页被 1.3k 人浏览