金三银四:滥竽充数多拿2k指北(上)

219 阅读9分钟

一、说明

声明:本文为原创文章,未经许可禁止转载

虽然有点标题党,但本文旨在帮助读者增加薪酬谈判的底气和筹码。

众所周知,现在前端的招聘加分项要求里多半会有一条:至少熟悉或了解一门后端语言(java/python/golang),如何让自己在这一项脱颖而出,顺利拿到offer,本文将会给你答案。

另外注意,简历上只需写自己最熟悉的一门后端语言就行,同时也从下面选择一个自己容易理解的语言。

开始前,给我的新公众号打个广告,欢迎关注:萌萌哒草头将军

image.png

二、怎么问你

作为前端,虽然经常和后端打交道,但是很难有机会系统学习某个后端语言,所幸,前端面试的时候一般会将JavaScript和Java两门语言的比较作为面试题目。比如:说说JavaScript和Java的异同点、JavaScript和Java相比有啥优点或缺点。

所以我们在面试前多做这类问题的准备。

全文包括代码7000+字太长了,所以将文章分为了上、下两篇。

上篇主要对比基础语法、面向对象编程的区别,下篇对比垃圾回收、异步编程和跨平台,下篇有大量的示意图辅助理解,所以下篇更精彩,下篇请轻戳我

下面就是你需要提前准备并且熟悉的知识点,

我将知识点分为了青铜黄金铂金,分别代表基础语法、语言特性、语言设计,面试的时候分别说出一两个点(十分推荐带星号标记的),这样粗中有细,有深有浅,面试官就会觉得你是真的了解而不是滥竽充数。如果被问道这个问题,你再假装思索一下(睁大眼睛,眼球向右上微微瞅一两秒)然后娓娓道来,效果更佳哦。

三、这么回答

青铜

1.语言类型

JavaScript是弱类型语言,边解释边执行,一个非const声明的变量可以保存多种类型的值。

// js
let num = 2022
let str = num.toString()
console.log(str)

Python作为弱类型语言,一个变量也可以保存任何类型的值,并且Python里没有定义常量的修饰符(可以自己实现)。

# python
num = 2022
# str = num.__str__()
# 等价于
str = str(num)
print(str)

而Java是强类型语言,先编译后执行,在编译前所有变量类型都是确定的,Java中如果一个变量接受另外类型的值,需要强制类型转换。另外,Java中使用final关键字定义不可变的常量

// java
// 声明前需要指定变量类型
Integer num = 2022;
String str = num.toString();
System.out.Println(str);

Golang亦然。Golang中也是用const定义常量

// go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	var a int = 2022
	// Golang数据类型自身没有方法(下面会提到),将数字转化为字符串,需要调用内建包`strconv`
	var b string =  strconv.Itoa(a)
	fmt.Println(b)
}
2.包装类*

JavaScript基本类型和包装类是一样的,基本类型可以调用类方法。

// js
let a = 2022
a.toString()

而Java基本类型无法调类方法,必须自动装箱成为包装类才能调用类型方法。

// java
int num = 2022
num.toString() // error

下面说明下Java的自动装箱和拆箱

image.png

// 没有自动装箱,声明一个包装类是这样的
Integer num1 = new Integer(2022);

// 有自动装箱,声明一个包装类是这样的
Integer num2 = 2022;

// 反过来就是自动拆箱
int num3 = num1;

// num1、num2是可以调用Integer类型方法的,比如toString,因为它们是包装类。
// num3是没法调用任何方法的,因为是基本类型。

Golang没有包装类的概念,基本类型(标量类型)没有方法可调用。

// go
var a int = 2022
// Golang数据类型自身没有方法(下面会提到),将数字转化为字符串,需要调用内建包`strconv`
var b string =  strconv.Itoa(a)

Python虽然没有包装类的概念,但是基本变量和JavaScript一样是可以调用类的内建方法。

# python
num = 2022
num = num.__str__()
3.number类型的差异*

JavaScript中数字类型只有number类型一种,而Java中数据类型字节数从小到大分为byte、short、int、long、double类型。所以JavaScript数字的转换是自动的,而Java中,小字节转换为大字节是自动转换,但是大字节转换为小字节,需要强制转换。

// java
byte a = 1;
// 由小到大
short b = a;

int c = 100;
// 由大到小需要手动指定
byte d = (byte)c;

如果说Java中的number类型很杂,那Golang中的number就是非常杂了,放个图感受下吧。

Golang中的数字转换必须是显示的。

// golang
var n1 = int32(8)
var n2 = int64(8)
var n3 = int64(n1) + n2
fmt.Println(n3) // 16

Python中的数字类型只有三种:int、float、complex,互相转换将数据类型作为函数名即可。

# python
a = 1.0
int(a)
print(a) # 1
4.==的区别*

JavaScript的==会进行隐式类型转换,然后按值比较。如果都是引用类型,只比较引用地址,如果是不同的类型,则会都会转换为同类型比较;

// js
0 == false // true

'1' == 1 // true

'hello' == 'he' + new String('llo') // true

而Java中==是严格按地址比较,地址相同时才相等,Java中的equals是严格按值比较的;同时Java中没有===

// java
0 == false // false

'1' === 1 // false

"hello" == "he" + new String("llo") // false

Golang中的==比较有意思,因为它既不像JavaScript一样可以隐式类型转换(不同类型比较会报错),也不完全像Java一样都按地址比较,对于基本类型,按值比较,对于聚合类型比较只有当字面完全相同时才相等,对于引用类型按地址比较,对于接口类型,则按接口类型(动态类型)类型值(动态值)比较,二者完全相同才相等;

package main

import (
	"fmt"
)

func main() {
	// golang
    // 基本类型
    var num1 = 1
    var num2 = 1
    fmt.Println(num1 == num2) // true

    // 聚合类型 数组
    a := [4]int{1, 2, 3, 4}
    b := [4]int{1, 2, 3, 4}
    c := [4]int{1, 3, 4, 5}
    fmt.Println(a == b) // true
    fmt.Println(a == c) // false
    // 聚合类型 结构体
    type A struct {
        a int
        b string
    }
    type B struct {
        a int
        b string
    }
    var d = A { a : 1, b : "1" }
    var e = A { a : 1, b : "1" }
    var f = A { a : 1, b : "2" }
    fmt.Println(d == e) // true
    fmt.Println(d == f) // false

    // 引用类型
    var h = &A { a : 1, b : "1" }
    var j = &A { a : 1, b : "1" }
    var k = h

    fmt.Println(h == j) // false
    fmt.Println(h == k) // true
}

Python中的==是按值比较的,is是按地址比较的。

# py
num1 = 444
num2 = 444
print(num1 == num2) # True
print(num1 is num2) # True

arr1 = [1, 2, 3]
arr2 = [1, 2, 3]
print(arr1 == arr2) # True
print(arr1 is arr2) # False
5.数组的异同

JavaScript和Python中的数组是任意长度的,并且可以存放各种类型。

// js
const arr = []
arr.push(...[1, '2', false, {}])
# python
arr = []
arr.append(1)
arr.append('2')

Java中的数组是固定长度的固定类型的(强类型语言的传统),如果要像JavaScript使用数组,需要使用Java集合框架里的ArrayList或者LinkedList。

// java
int[] arr = new int[5];
arr[0] = 1;
arr[1] = 2;

List<Object> list = new ArrayList<Object>();
list.add(1);
list.add("2");

Golang也有这个“毛病”,所以Golang推出了切片的概念:长度可变的数组就是切片,所以也被成为动态数组

// golang中,空接口泛指一切类型,所以golang没有泛型
type iface interface {}

// 定义数组的话需要指定长度
// var arr [3]iface 
// 或者用...代表长度
// var arr [...]iface{1, 2, 3}
// 定义切片就不需要指定长度了
var nums []iface
nums = append(nums, 1)
nums = append(nums, '2')

黄金

6.面向对象*

JavaScript虽然可以面向对象编程,但是它不符合面向对象编程的编程方式,class语法仅仅是prototype的语法糖,而Java是标准的面向对象的编程语言,天生具有面向对象特性:封装、继承、多态(多态的表现:重载、重写),JavaScript中没有封装和多态,所以没有重载,JavaScript的“重写”仅仅是prototype“短路”假象(因为子类有这个方法就不会沿着prototype属性向上查询)

// java
class Animal {
	String name;
	int age;
}
class Cat extends Animal {
	int fish;
	public Cat (String name, int age, int fish) {
		this.name = name;
		this.age = age;
		this.fish = fish;
	}
	public void call () { System.out.println("I am "+ this.name +", I have " + this.fish + "小鱼干"); }
}

class Dog extends Animal {
	int bone;
	public void Cat (String name, int age, int bone) {
		this.name = name;
		this.age = age;
		this.bone = bone;
	}
	public void call () { System.out.println("I am "+ this.name +", I have " + this.bone + "小鱼干"); }
}

public static void main (String []args) {
	Cat cat = new Cat("cat", 1, 2);
	cat.call();
	
	Dog dog = new Dog("dog", 2, 3);
	dog.call();
}

Python没有extends关键字,继承的时候只需要在子类后面指定父类,同时支持多继承。Python的__init__和JavaScript的constructor不仅在写法上十分相似,而且都是实例化时自动执行的。

class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Cat(Animal):
    def __init__(self, name, age, fish):
        Animal.__init__(self, name, age)
        self.fish = fish
    
    def call(self):
        print("I am " + self.name + ", I have " + str(self.fish) + "小鱼干")

class Dog(Animal):
    def __init__(self, name, age, bone):
        Animal.__init__(self, name, age)
        self.bone = bone
    
    def call(self):
        print("I am " + self.name + ", I have " + str(self.bone) + "大骨头")

cat = Cat("cat", 1, 2)
cat.call() # I am cat, I have 2小鱼干

dog = Dog("dog", 2, 3)
dog.call() # I am dog, I have 3大骨头

Golang没有类的概念,只有结构体strut和接口interface,通过面向接口编程、结构体嵌入式开发达到面向对象的效果。嵌入式继承的写法确实很简洁,而且成员方法在类外部定义的。

package main
import "fmt"
type AnimalInterface interface {
    call()
}
type Animal struct {
    name string
    age int32
}
type Cat struct {
    Animal
    fish int32
}
type Dog struct {
    Animal
    bone int32
}
func (this Cat) call() {
    fmt.Println("I am", this.name, "I have ", this.fish, "小鱼干")
}

func (this Dog) call() {
    fmt.Println("I am", this.name, "I have ", this.bone, "大骨头")
}

func main() {
	var cat AnimalInterface = Cat{Animal{"cat", 1}, 2}
    cat.call() // I am cat I have  2 小鱼干
    
	var dog AnimalInterface = Dog{Animal{"dog", 2}, 3}
    dog.call() // I am dog I have  3 大骨头
    
    // Go里面可以面向struct编程,但是Go不鼓励这么做
    var cat2 = Cat{Animal{"cat", 1}, 2}
	cat2.call() // I am cat I have  2 小鱼干
}
7. 私有属性和公有属性

JavaScript使用对象的defineProperty或者proxy方法可以限制对象属性私有还是公有。

// js
Object.defineProperty(window, 'pi', {
     writable:false,
     value: 3.14
})

Java中使用修饰符private或者public控制。

// java
// 公有
public float pi = 3.14

// 私有private float pi = 3.14

Python可以通过在变量前面加双下划线__表示私有。

# python
pi = 3.14 #公有
__pi = 3.14 #私有

Golang中通过大写表示共有属性。小写或者下划线开头表示私有。

// go
var pi float32 = 3.14 // 私有属性
var _pi float32 = 3.14 // 私有属性
var PI float32 = 3.14 // 公有属性

好了,上篇到此就结束了,更多有趣的对比欢迎戳我下篇,或者关注我的微信公众号:萌萌哒草头将军

image.png

下篇直达点我

谢谢各位的捧场

往期推荐
优美的v-for列表加载动画:vue动画钩子实践
我的代码简洁之道