「译」精通Kotlin标准函数:run、with、let、also和apply

原文地址:https://medium.com/@elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84

一些 Kotlin 的标准函数非常相似,以至于我们都无法确定要使用哪一个。这里我会介绍一种简单的方式来区分他们的不同点以及如何选择使用。

作用域函数

接下来聚焦的函数有:runwithT.runT.letT.also 以及 T.apply。我称他们为作用域函数(scoping functions),因为它们为调用方函数提供了一个内部作用域。

最能够体现作用域的是 run 函数:

1
2
3
4
5
6
7
8
9
10
fun test() {
var mode = "I am sad"

run {
val mood = "I am happy"
println(mood) // I am happy
}

println(mood) // I am sad
}

基于此,在 test 函数内部,你可以拥有一个单独的区域,在这个作用域内,mood 在打印之前被重新定义成了 I am happy,并且它完全被包裹(enclosed)在 run 的区域内。

这个作用域函数本身看起来并不会非常有用。但是除了拥有单独的区域之外,它还有另一个优势:它有返回值,即区域内的最后一个对象。

因此,下面的代码会变得整洁,我们把 show() 函数应用到两个 view 之上,但是并不需要调用两次。

1
2
3
run {
if (firstTimeView) introView else normalView
}.show()

这里演示所用,其实还可以简化为 (if (firstTimeView) introView else normalView).show()

作用域函数三大特性

为了让作用域函数更有意思,可将其行为分类为三大特性。我会使用这些特性来区分彼此。

一、正常 vs. 扩展函数

如果我们看一下 withT.run,会发现它们的确非常相似。下面的代码做了同样的事情。

1
2
3
4
5
6
7
8
9
10
11
with(webview.settings) {
javaScriptEnabled = true
databaseEnabled = true
}

// similarly

webview.settings.run {
javaScriptEnabled = true
databaseEnabled = true
}

但是,它们的不同点在于,一个是正常函数(即 with),另一个是扩展函数(即 T.run)。

假设 webview.settings 可能为空,那么代码就会变成下面的样子:

1
2
3
4
5
6
7
8
9
10
11
// Yack!
with(webview.settings) {
this?.javaScriptEnabled = true
this?.databaseEnabled = true
}

// Nice
webview.settings?.run {
javaScriptEnabled = true
databaseEnabled = true
}

在这个案例中,T.run 的扩展函数明显要好一些,因为我们可以在使用前就做好了空检查。

二、this vs. it 参数

如果我们看一下 T.runT.let,会发现两个函数是相似的,只有一点不同:它们接收参数的方式。下面代码展示了用两个函数实现同样的逻辑:

1
2
3
4
5
6
7
8
9
stringVariable?.run {
println("The length of this String is $length")
}

// Similarly

stringVariable?.let {
println("The length of this String is ${it.length}")
}

如果检查一下 T.run 的函数签名就会发现 T.run 只是一个调用 block: T.() 的扩展函数。因此在它的作用域内,T 可以被引用为 this。实际编程中,this 大部分情况下都可以被省略。因此,在上面的例子中,我们可以在 println 的声明语句中使用 $length 而不是 ${this.length}。我把它称之为:this 作为参数进行传递。

但是,对于 T.let 函数,你会发现 T.let 把它自己传入了函数 block: (T)。因此它被当做一个 lambda 参数来传递。在作用域函数内它可以被引用为 it。所以我称之为:it 作为参数进行传递。

从上面可以看出,T.run 好像比 T.let 高级,因为它更隐式一些,但是 T.let 函数会有些一些微妙的优势:

  • T.let 可以更清楚地区分所得变量和外部类的函数/成员。

  • this 不能被省略的情况下,例如用作一个函数参数,itthis 更短更清晰。

  • T.let 允许用更好的命名来表示转换过的所用变量(the converted used variable),也就是说,你可以把 it 转换为其他名字:

    1
    2
    3
    4
    stringVariable?.let {
    nonNullString ->
    println("The non null string is $nonNullString")
    }

三、返回 this vs. 其他类型

现在,我们看一下 T.letT.also,如果我们看一下函数作用域内部的话,会发现两者是一样的:

1
2
3
4
5
6
7
8
9
stringVariable?.let {
println("The length of this String is ${it.length}")
}

// Exactly the same as below

stringVariable?.also {
println("The length of this String is ${it.length}")
}

但是,它们微妙的区别之处在于返回了什么。T.let 返回了一个不同类型的值,但是 T.also 返回了 T 自身,也就是 this

简单的示例如下:

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
val original = "abc"

// Evolve the value and send to the next chain
original.let {
println("The original String is $it") // "abc"
it.reversed() // evolve it as parameter to send to next let
}.let {
println("The reverse String is $it") // "cba"
it.length // can be evolve to other type
}.let {
println("The length of the String is $it") // 3
}

// Wrong
// Same value is sent in the chain (printed answer is wrong)
original.also {
println("The original String is $it") // "abc"
it.reversed() // even if we evolve it, it is useless
}.also {
println("The reverse String is ${it}") // "abc"
it.length // even if we evolve it, it is useless
}.also {
println("The length of the String is ${it}") // "abc"
}

// Corrected for also (i.e. manipulate as original string
// Same value is sent in the chain
original.also {
println("The original String is $it") // "abc"
}.also {
println("The reverse String is ${it.reversed()}") // "cba"
}.also {
println("The length of the String is ${it.length}") // 3
}

上面的 T.also 貌似没什么意义,因为我们可以轻松把它们组合进一个单一的函数块内。仔细想一下,它们会有如下优势:

  • 它可以为相同的对象提供清晰的处理流程,可以使用粒度更小的函数式部分。
  • 它可以在被使用之前做灵活的自处理(self manipulation),可以创建一个链式构造器操作。

如果两者结合链式来使用,一个进化自己,一个持有自己,就会变得非常强大,例如:

1
2
3
4
5
6
7
8
9
// Normal approach
fun makeDir(path: String): File {
val result = File(path)
result.mkdirs()
return result
}

// Improved approach
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }

回顾一下所有的特性

通过这三个特性,我们可以清楚地知道每个函数的行为。让我们举例说明一下上面没有提到的 T.apply 函数,它的 3 个特性如下所述:

  • 它是一个扩展函数
  • 它把 this 作为参数
  • 它返回了 this(它自己)
1
2
3
4
5
6
7
8
9
10
11
12
// Normal approach
fun createIntent(intentData: String, intentAction: String): Intent {
val intent = Intent()
intent.action = intentAction
intent.data = Uri.parse(intentData)
return intent
}

// Improved approach, chaining
fun createIntent(intentData: String, intentAction: String) =
Intent().apply { action = intentAction }
.apply { data = Uri.parse(intentData) }

或者我们也可以把一个非链式的对象创建过程变得可链式(chain-able):

1
2
3
4
5
6
7
8
9
10
11
12
// Normal approach
fun createIntent(intentData: String, intentAction: String): Intent {
val intent = Intent()
intent.action = intentAction
intent.data = Uri.parse(intentData)
return intent
}

// Improved approach, chaining
fun createIntent(intentData: String, intentAction: String) =
Intent().apply { action = intentAction }
.apply { data = Uri.parse(intentData) }

函数选择

现在思路变清晰了,根据这三大特性,我们可以对函数进行分类。基于此可以构建一个决策树来帮助我们根据需要来选择使用哪一个函数。

希望上面的决策树能够更清晰地阐述这些函数,同时也能简化你的决策,使你能够得当地使用这些函数。

Kotlin 中 var、val、const 关键字解析

昨天公众号后台收到一位小伙伴的留言询问,他对于 Kotlin 为何没有 Java 的 final 关键字感到困惑,这应该是很多初学者都会遇到的问题,所以我就写了这篇博文从更底层的角度来解析 Kotlin 声明变量时用到的三个关键字:varvalconst

其实,Java 的 final 就等价于 Kotlin 的 val, 虽然通过 javap 反编译可以看到两者的底层实现不一样,但是从语义上讲,它们两者的确是等价的。具体原因,我们来逐一分析。

什么是属性

我们知道,在 Kotlin 的世界中,class 已经不再是唯一的一等公民,我们可以直接在代码文件的最顶层(top-level)声明类、函数和变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Address {
// class properties
var province = "zhejiang"
val city = "hangzhou"
}

fun prettify(address: Address): String {
// local variable
val district = "xihu"
return district + ',' + address.city + ',' + address.province
}

// top-level property
val author = "liangfei"

上例中的 Address 是一个类,prettify 是一个函数,author 是一个变量,它们都是一等公民,也就是说,函数和变量可以单独存在,不会像 Java 那样依附于类。

首先,varval 可分为三种类型:

  • 类的属性(class property),例如上例中的 var province = "zhejiang",它是 Address 类的一个属性;
  • 顶层属性(top-level property),例如上例中的 val author = "liangfei",它是文件(module)的一个属性;
  • 局部变量(local variable),例如上例中的 val district = "xihu",它是函数 prettify 的一个局部变量。

类的属性和顶层属性都是属性,所以可以统一来看待,属性本身不会存储值,也就是说它不是一个字段(field),那它的值是哪里来的呢?我们先来看一下声明一个属性的完整语法:

1
2
3
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]

可以看出,一个属性的声明可以分解为五个部分:属性名、属性类型、initializer、getter、setter。

  • 属性的名就是就是我们用来引用属性的方式;
  • 属性的类型可以显示声明,因为 Kotlin 支持类型推导,如果类型能够从上下文推导得出,那么它也可以省略;
  • initializer 是类型推导的线索之一,例如 val author = "liangfei",根据 = "liangfei" 可以得出它是一个 String 类型;
  • getter 也是类型推导的线索之一,所有使用属性名获取值的操作,都是通过 getter 来完成的;
  • setter 用于给属性赋值。

以上只是声明了一个属性,如果我们要赋值,它的值会存储在哪里呢?其实,编译器还会自动为属性生成一个用于存储值的字段(field),因为写代码时感知不到到它的存在,所以称为幕后字段(backing field)。具体可以参考幕后字段,因为与本文关系不大,所以此处不做介绍。

varval 所声明的属性,其最本质的区别就是:**val 不能有 setter**,这就达到了 Java 中 final 的效果。

例如,上面 Kotlin 代码中的 Address 类:

1
2
3
4
class Address {
var province = "zhejiang"
val city = "hangzhou"
}

它在 JVM 平台上的实现是下面这样的(通过 javap 命令查看):

1
2
3
4
5
6
public final class Address {
public final java.lang.String getProvince();
public final void setProvince(java.lang.String);
public final java.lang.String getCity();
public Address();
}

可以看出,针对 var province 属性,生成了 getProvince()setProvince(java.lang.String) 两个函数。但是 val city 只生成了一个 getCity() 函数。

对于局部变量来说,var 或者 val 都无法生成 getter 或 setter,所以只会在编译阶段做检查。

看一下它的官方定义(中文版可参考属性和字段):

Classes in Kotlin can have properties. These can be declared as mutable, using the var keyword or read-only using the val keyword.

对于类的属性来说:var 表示可变(mutable),val 表示只读(read-only)。对于顶层属性来说也是一样的。

可变和只读

var 表示可变,val 表示只读,而不是不可变(immutable)。我们已经知道了 val 属性只有 getter,但是这并不能保证它的值是不可变的。例如,下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person {
var name = "liangfei"
var age = 30

val nickname: String
get() {
return if (age > 30) "laoliang" else "xiaoliang"
}

fun grow() {
age += 1
}
}

属性 nickname 的值并非不可变,当调用 grow() 方法时,它的值会从 "laoliang" 变为 "xiaoliang",但是无法直接给 nickname 赋值,也就是说,它不能位于赋值运算的左侧,只能位于右侧,这就说明了为什么它是只读(read-only),而不是不可变(immutable)。

其实,Kotlin 有专门的语法来定义可变和不可变的变量,后面会专门写一篇博问来分析,这里不再深入。

我们知道,Java 中可以使用 static final 来定义常量,这个常量会存放于全局常量区,这样编译器会针对这些变量做一些优化,例如,有三个字符串常量,他们的值是一样的,那么就可以让这个三个变量指向同一块空间。我们还知道,局部变量无法声明为 static final,因为局部变量会存放在栈区,它会随着调用的结束而销毁。

Kotlin 引入一个新的关键字 const 来定义常量,但是这个常量跟 Java 的 static final 是有所区别的,如果它的值无法在编译时确定,则编译不过,因此 const 所定义的常量叫编译时常量

编译时常量

首先,const 无法定义局部变量,除了局部变量位于栈区这个原因之外,还因为局部变量的值无法在编译期间确定,因此,const 只能修饰属性(类属性、顶层属性)。

因为 const 变量的值必须在编译期间确定下来,所以它的类型只能是 String 或基本类型,并且不能有自定义的 getter。

所以,编译时常量需要满足如下条件:

  • 顶层或者 object 的成员(object 也是 Kotlin 的一个新特性,具体可参考对象声明)。
  • 初始化为一个 String 或者基本类型的值
  • 没有自定义 getter

总结

最后,总结一下:

  • varval 声明的变量分为三种类型:顶层属性、类属性和局部变量;
  • var 属性可以生成 getter 和 setter,是可变的(mutable),val 属性只有 getter,是只读的(read-only,注意不是 immutable);
  • 局部变量只是一个普通变量,不会有 getter 和 setter,它的 val 等价于 Java 的 final,在编译时做检查。
  • const 只能修饰没有自定义 getter 的 val 属性,而且它的值必须在编译时确定。

参考资料

Sass 和 Less 的用法和比较

SassLess 是两种 CSS 预处理器,扩展了 CSS 语法,目的都是为了让 CSS 更容易维护。

Sass

Sass 有两种语法,最常用是的 SCSS(Sassy CSS),是 CSS3 的超集。另一个语法是 SASS(老的,缩进语法,类 Python)。

Sass 的预处理器工具是 sass:

1
2
3
4
5
6
7
8
# 单个文件
sass input.sass output.css

# 监控
sass --watch input.sass output.css

# 监控目录
sass --watch app/sass:public/stylesheets

Sass 扩展了 css 的特性:

  • 变量
  • 嵌套
  • 混合(mixin)
  • 继承

变量

1
2
3
4
5
6
7
$font-stack:        Helvetica, sans-serif;
$primary-color: #333;

body {
font: 100% $font-stack;
color: $primary-color;
}

嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
nav {
ul {
margin: 0;
padding: 0;
list-style: none;
}

li { display: inline-block; }

a {
display: block;
padding: 6px 12px;
text-decoration: none;
}
}

局部(Partials)

局部 Sass 文件不会被翻译成 css 文件,命名规范是下划线开头——_partial.scss,可以用 @import 导入。

css 也可以模块化

导入

CSS 也有 import,但是会带来 HTTP 请求开销。Sass 的 @import 只是合并文件,不会发请求。

1
2
3
4
5
6
7
8
// _reset.cscc
html,
body,
ul,
ol {
margin: 0;
padding: 0;
}
1
2
3
4
5
6
7
8
// base.scss

@import 'reset';

body {
font: 100% Helvetica, sans-serif;
background-color: #efefef;
}

混合(Mixin)

Mixins are a way of including (“mixing in”) a bunch of properties from one rule-set into another rule-set.

Mixin 特别适用于处理 vendor prefiex。

1
2
3
4
5
6
7
8
@mixin border-radius($radius) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
-ms-border-radius: $radius;
border-radius: $radius;
}

.box { @include border-radius(10px); }

扩展/继承

@extend 可以为 CSS 的属性 “提取公因式”,这个“公因式”叫占位类(placeholder class)。

如果占位类没有并用到就不会输出到 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
// This CSS won't print because %equal-height is never extended.
$equal-heights {
display: flex;
flex-wrap: wrap;
}

// This CSS will print because $message-shared is extended
$message-shared {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}

.message {
@extend $message-shared;
}

.success {
@extend $message-shared;
border-color: green;
}

.error {
@extend $message-shared;
border-color: red;
}

.warning {
@extend $message-shared;
border-color: yellow;
}

处理后的 CSS 文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.message, .success, .error, .warning {
border: 1px solid #cccccc;
padding: 10px;
color: #333;
}

.success {
border-color: green;
}

.error {
border-color: red;
}

.warning {
border-color: yellow;
}

操作符

支持数学运算。

1
2
3
4
5
6
7
8
9
10
11
.container { width: 100%; }

article[role="main"] {
float: left;
width: 600px / 960px * 100%;
}

aside[role="complementary"] {
float: right;
width: 300px / 960px * 100%;
}

处理过后的 CSS 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
.container {
width: 100%;
}

article[role="main"] {
float: left;
width: 62.5%;
}

aside[role="complementary"] {
float: right;
width: 31.25%;
}

Less

相比于 Sass,Less 的预处理支持压缩。

1
lessc --clean-css styles.less styles.min.css

Less 还可以直接用在 Node 环境:

1
2
3
4
5
var less = require('less');

less.render('.class { width: (1 + 1) }'), function (e, output) {
console.log(output.css);
});

打印结果:

1
2
3
.class {
width: 2;
}

Less 还支持传入配置:

1
2
3
4
5
6
7
8
9
var less = require('less');
less.render('.class { width: (1 + 1) }',
{
paths: ['.', './lib'], // Specify search paths for @import directives
filename: 'style.less', // Specify a filename, for better error messages
},
function(e, output) {
console.log(output.css);
});

还可以直接用于端侧,不推荐用于生产环境,有性能开销。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- set options before less.js script -->
<link rel="stylesheet/less" type="text/css" href="styles.less" />

<script>
less = {
env: "development",
async: false,
fileAsync: false,
poll: 1000,
functions: {},
dumpLineNumbers: "comments",
relativeUrls: false,
rootpath: ":/a.com/"
};
</script>

<script src="less.js"></script>

tips

下面代码中的 & 表示父选择器:

1
2
3
4
5
6
7
8
9
10
11
12
13
.clearfix {
display: block;
zoom: 1;

$:after {
content: " ";
display: block;
font-size: 0;
height: 0;
clear: both;
visiblity: hidden;
}
}

Vim 学习笔记

Command-Line Mode

Tip 27 Meet Vim’s Command Line

  • :[range]delete [x]
  • :[range]yank [x]
  • :[line]put [x]
  • :[range]copy {address}
  • :[range]move {address}
  • :[range]join
  • :[range]normal {commands}
  • :[range]substitue/{pattern}/{string}/[flags]
  • :[range]global/{pattern}/[cmd]
  • :edit/:write
  • :tabnew
  • :split
  • :prev/:next
  • :bprev/:bnext
  • :h ex-cmd-index

Tip 28 Execute a Command on One or More Consecutive Lines

  • :print
  • :delete
  • :join
  • :substitute
  • :normal
  • :3 <=> 3G
  • :3d <=> 3G dd
  • :{start},{end}
  • . = the current line
  • $ = the end line
  • % = all the lines

面试问题大汇总(不断更新)

Android

  • Activity & Fragment
    • Activity的生命周期
      • onDestroy一定会被执行吗?
      • onStop中能更新数据吗?
    • Activity的启动模式
      • 什么情况下Activity的onNewIntent会执行?
    • Fragment能否不依赖Activity存在?
    • 描述一下Framgent的栈管理机制?
    • Activity和Fragment如何通信?
    • 如果后台的Activity由于某原因被系统回收了,如何在被系统回收之前保存当前状态?
    • Activity间通过Intent传递数据大小有没有限制?
  • Service
    • Service有几种启动方式?
    • Service的生命周期?
    • Service和IntentService的区别是什么?
  • ContentProvider & 数据
    • 请介绍下ContentProvider是如何实现数据共享的
    • 请介绍下Android的数据存储方式
  • Intent
    • 什么是显示intent(explicit intent)、什么是隐式intent(implicit intent)?
    • intent-filter的组成部分有哪些?
  • View & Layout
    • View的绘制流程
    • Touch时间的传递机制
    • Android中的几种动画
      • FrameAnimation
      • TweenAnimation
      • PropertyAnimation
    • Android中常用的布局
      • RelativeLayout & LinearLayout & ConstraintLayout
    • Activity、View、Window之间的关系是什么?
  • Android中跨进程通讯有几种方式(多进程
    • 访问其他应用程序的Activity
    • Content Provider
    • 广播(Broadcast)
    • AIDL
  • 线程
    • 单线程模型中Message,Handler,Message Queue,Looper之间的关系
    • Thread和HandlerThread的区别是什么?
    • 造成ANR的原因是什么?
  • 内存
    • 内存溢出和内存泄漏有什么区别?
    • 何时会产生内存泄漏?
    • 内存优化有哪些方法?
  • 设计模式
    • Android 中常见的设计模式有哪些?
    • OkHttp的interceptor是什么设计模式?chain of responsibility
    • 什么是AOP?
  • 其他
    • asset 和 raw 文件夹的区别是什么?
    • ApplicationId和PackageName的区别是什么?
    • 说一个jsbridge的实现方式?
    • DVM和JVM的区别是什么?
      • Android 的类加载机制?ClassLoader
    • Android最新版本是什么?有哪些新特性?
    • version_code和version_name的区别是什么?

协议

  • http的method有哪些?
  • https与http的区别是什么?
  • 能描述一下TLS握手的过程吗?
  • 什么是中间人攻击(MIMA)?
  • 什么是对称加密、什么是非对称加密?
  • OAuth2的client_id和client_secret是干什么用的?
  • 如何在http层面优化网络?

工具类

  • git怎么暂时保存更改

Java

  • Java中volatile的作用是什么?
  • 注解 Annotation
    • 生命周期有哪些?
    • 作用对象有哪些?
  • 不应该被序列化的字段用什么关键字标记?- transient
  • WeakReference和WeakReference的区别
  • synchronized的用法(类和对象)
  • 为什么内部类会持有外部类的引用?
  • 怎样继承一个内部类?
  • 内部类的构造方法是什么?
  • Java中try catch finally的执行顺序?
  • Retrofit中如何使用了动态代理?
  • RxJava的线程模型是什么?

FP

  • 闭包是什么?lambda是一个闭包吗?
  • FP的有哪些特性?
  • Reactive 用到了FP的哪些特性?

参考资料

管理学习日记(二)

你印象最深刻的是什么

管理七原则之一——打造信任

你的感受如何?哪里感觉很好、很满意、很有感触?有什么联想?

因为信任,所以简单。信任能够消除不必要的揣测,让沟通变得更直接、更简单,而且有效的激励也是建立在信任的基础之上。

信任是双向的,管理者要信任下属,下属也要信任管理者。只有内在的真诚才能建立真正的信任,因为人与人之间的沟通,其实很容易能够感受到对方是真诚还是虚伪。互相的信任能够带来更多的安全感,有了安全感,我们才能更忠于自己的内心,做决策或做任务时就不会瞻前顾后。

打造信任让我联想到了一个诚信社会应该有的样子:

  • 进超市买东西不用存包,因为他们相信我不会偷东西
  • 进饭馆可以安心吃东西,因为我相信他们不会使用地沟油
  • 丢了东西也不会担心,因为我相信警察叔叔能帮我找到
  • 看见别人跌倒了也会上前去扶,因为我相信不会被讹上

构筑在诚信之上的社会会让每个人都活得放松、自然。

我学习到了什么?领悟了什么?发现了什么?主要体会是什么?

建立诚信必须要拿出勇气真诚

虽然信任很重要,但是过犹不及,盲目的信任其实是在逃避责任,信任不是做甩手掌柜。

以身作则、坚定信念,共同的价值观有助于建立彼此间的信任。

到目前为止,你觉得回去需要:

  • 开始做什么?
    • 来一次走心的一对一沟通
    • 发自内心地相信别人可以做得更好
  • 停止做什么?
    • 打击责备别人
  • 调整和改进什么?
    • 更多的放权
    • 更多的鼓励
    • 向上管理

管理学习日记(一)

印象最深刻的点

TL的吐槽环节中,每个小组都提出了很多槽点,有点罄竹难书的意思。

其实吐槽别人的同时,我们也需要时刻反思自己有没有中枪,对TL的不满在某种程度上能反映出自己内心想要的东西,因为能明确地指责别人,说明自己是在意这件事的。

很多时候,说者无心、听者有意,因为认知的差异会导致理解偏差,所以我们在与他人沟通时要多站在对方角度考虑问题,同理心和换位思考的能力非常重要。尽量追根溯源,弄清楚对方思考的出发点(也就是WHY)更有助于我们找到问题的根本原因。

对于大部分普通人来说,知易行难,也就是说道理我都懂,但就是不懂如何去执行,所以工具和方法论很重要。这次培训对我来说最大的意义是:能够学会如何拆解困境并找到破局之道。 又让我联想到以前在FUJITSU工作时,故障复盘时经常用到一个叫「根本原因分析」的方法:

这个方法要求我们能不断地追问自己,打破砂锅问到底,不要停留在问题表面,尽量去寻找本质。当然做不到「深度心理学」的专业性,但是方向至少是对的。

收获和启发

课程最后所讲的「结果导向」深有感触。

老板有一次在员工大会上说过:我们为过程鼓掌,但是只为结果买单。管理者带领团队的第一要义就是要拿结果,这个结果是真正意义上的“果实”,举个栗子:果农辛苦劳作一年,果树打理的也很漂亮,但是如果没有收成,那么这一年就无法创收。

也就是说,我们在执行OKR时,即使KR完成得很漂亮,如果O没有达成,依然是无法满足期望,这也证明了KR及时调整的重要性。

目标刻在岩石上、计划写在沙滩上

具有挑战性的O可以有效激励有追求的人去拿到「超出期望」的结果,取得高绩效。但是“激励”要因人而异, 要去分析每个团队成员的“需求”,“复盘”、“团建”以及“一对一沟通”可以有效帮助我们了解团队成员。

过去一年仅仅在“按部就班”地执行管理工作,一边总结一边回想着之前做的事,有一种“认知升华”的感觉,貌似看懂了很多管理动作背后的动机。:)

管理是一门科学,2018年我要好好学一学

小程序开发总结

小程序的核心是一个响应式的数据绑定系统,逻辑上分为视图层和逻辑层。这两层始终保持同步,只要在逻辑层修改数据,视图层就会相应的更新。

小程序遵守Convention Over Configuration的原则,没有留给开发者自由选择的余地。

支付宝小程序和微信小程序的开发框架几乎一模一样,以下代码以微信小程序为例。