Kotlin 扩展函数 与 JS 的 prototype

简介: Kotlin 扩展函数 与 JS 的 prototypeKotlin 扩展函数Kotlin的扩展函数功能使得我们可以为现有的类添加新的函数,实现某一具体功能 。

Kotlin 扩展函数 与 JS 的 prototype

Kotlin 扩展函数

Kotlin的扩展函数功能使得我们可以为现有的类添加新的函数,实现某一具体功能 。
扩展函数是静态解析的,并未对原类添加函数或属性,对类本身没有任何影响。
扩展属性允许定义在类或者kotlin文件中,不允许定义在函数中。

lambda是要作为参数被传入某方法或赋值给某变量的匿名方法的简化表现形式。

fun Activity.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT){
    Toast.makeText(this, message, duration).show()
}

this指接收者对象(receiver object)(也就是调用扩展函数时, 在"."号之前指定的对象实例).

fun Any?.toString():String{
    if(this == null)
        return "null"
    else{
        return toString()
    }
}

1.扩展(extensions)

在不修改原类的情况下,
Kotlin能给一个类扩展新功能,无需继承该类,也不用任何设计模式(如装饰模式等),
Kotlin支持扩展函数和扩展属性!

为什么要使用扩展(动机):
在Java中,有很多工具类如java.util.Collections,使用很繁琐:

// Java
Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list))

静态导入Collections类,简化写法:

// Java
swap(list, binarySearch(list, max(otherList)), max(list))

静态导入使用依然很麻烦,如果能给list类添加扩展函数就好了:

list.swap(list.binarySearch(otherList.max()), list.max())

2.类-扩展函数

1.定义

为MutableList类扩展一个swap函数:

    fun MutableList<Int>.swap(index1: Int, index2: Int) {
        val tmp = this[index1]  //this: 当前MutableList对象 
        this[index1] = this[index2]
        this[index2] = tmp
    }    

对MutableList对象调用swap函数:

val list = mutableListOf(1, 2, 3)
list.swap(0, 2)

MutableList泛化类型:

    //为在表达式中使用泛型,要在函数名前添加泛型参数!
    fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
        val tmp = this[index1]
        this[index1] = this[index2]
        this[index2] = tmp
    }

2.静态解析(没有多态)

扩展不能真正修改类,即没有在一个类中插入新成员!
扩展函数是静态解析分发的,不是虚函数(即没有多态),调用只取决于对象的声明类型!
1.调用是由对象声明类型决定,而不是由对象实际类型决定!

    open class C
    class D: C()
    fun C.foo() = "c"
    fun D.foo() = "d"
    fun printFoo(c: C) {
        println(c.foo()) //扩展函数是静态解析的,不是虚函数(即没有多态)
    }
    fun main(args: Array<String>) {
        printFoo(D()) //输出"c",扩展函数调用只取决于参数c的声明类型 
    }

2.类的成员函数和扩展函数-同名同参数:

    class C {
        fun foo() { println("member") }
    }
    fun C.foo() {
        println("extension") 
    }
    fun main(args: Array<String>) {
        val c = C()
        println(c.foo()) //输出“member”
    }

3.类的成员函数和扩展函数-同名不同参数:

    class C {
        fun foo() { println("member") }
    }
    fun C.foo(i: Int) {
        println("extension")
    }
    fun main(args: Array<String>) {
        val c = C()
        println(c.foo(2)) //输出"extension"
    }

3.可空接收者

可null的类型定义扩展,即使对象为null,也可在对象上调用!

 fun Any?.toString(): String {
        if (this == null) return "null"
        return toString()
    }

2.类-扩展属性

和扩展函数类似,Kotlin也支持扩展属性:

    val <T> List<T>.lastIndex: Int // 不能初始化
        get() = size - 1 // 只能由getters/setters显式提供

    val Foo.bar = 1 // 错误:扩展属性不能有初始化器
        get() = 1

由于扩展没有在类中插入新成员,因此扩展属性无法使用幕后字段,
这就是为什么扩展属性不能有初始化器,只能由getters/setters显式提供!
3.伴生对象-扩展函数和属性

可为伴生对象定义扩展函数和属性:

    class MyClass {
        companion object { }  //伴生对象
    }

    fun MyClass.Companion.foo() {
        // ……
    }

    MyClass.foo() //用类名调用

4.作用域

1.扩展直接在包中

在顶层定义扩展(即直接在包中):

    package foo.bar
    fun Baz.goo() {
        ...            
    }

在其它包调用:

    package com.example.usage
    import foo.bar.goo //导入所有名为“goo”的扩展
    // 或者 import foo.bar.*
    fun usage(baz: Baz) {
        baz.goo()
    }

2.扩展作为类成员

在一个类内部可为另一个类声明扩展,
扩展声明所在的类称为分发接收者(dispatch receiver),
扩展函数调用所在类称为扩展接收者(extension receiver)

1.定义

    class D { //扩展接收者(extension receiver)
        fun f() { …… }
    }

    class C { //分发接收者(dispatch receiver)
        fun f() { …… }

        fun D.foo() {
           this@C.f() //分发接收者 C.f()
           f()        //扩展接收者 D.f()
        }

        fun call(d: D) {
            d.foo()   //调用扩展函数
        }
    }

2.继承-覆盖
成员扩展可声明为open,并在子类中被覆盖,
对分发接收者是虚拟的(多态),但对扩展接收者是静态的!

    open class D {
    }
    class D1 : D() {
    }

    open class C {
        open fun D.foo() {
            println("D.foo in C")
        }
        open fun D1.foo() {
            println("D1.foo in C")
        }
        fun call(d: D) {
            d.foo()   // 调用扩展函数
        }
    }
    class C1 : C() {
        override fun D.foo() {
            println("D.foo in C1")
        }
        override fun D1.foo() {
            println("D1.foo in C1")
        }
    }
    C().call(D())   // 输出 "D.foo in C"
    C().call(D1())  // 输出 "D.foo in C", 扩展接收者静态解析(非多态)
    C1().call(D())  // 输出 "D.foo in C1",分发接收者虚拟解析(多态)


JS 的 prototype

JavaScript prototype 属性

定义和用法

prototype 属性使您有能力向对象添加属性和方法。
prototype就是“一个给类的对象添加方法的方法”,使用prototype属性,可以给类动态地添加方法

语法

object.prototype.name=value

实例

在本例中,我们将展示如何使用 prototype 属性来向对象添加属性:

function employee(name,job,born){
  this.name=name;
  this.job=job;
  this.born=born;
}
var bill=new employee("Bill Gates","Engineer",1985);
employee.prototype.salary=null;
bill.salary=20000;
console.log(bill.salary);

输出:

20000

相关文章
|
5天前
|
前端开发 JavaScript 数据处理
在JavaScript中,异步函数是指那些不会立即执行完毕,而是会在未来的某个时间点(比如某个操作完成后,或者某个事件触发后)才完成其执行的函数
【6月更文挑战第15天】JavaScript中的异步函数用于处理非同步任务,如网络请求或定时操作。它们使用回调、Promise或async/await。
21 7
|
1天前
|
设计模式 JavaScript 前端开发
JS 代码中变量和函数的正确写法总结
**代码规范与最佳实践摘要** 1. 使用可读性强的变量名,如`currentDate`代替`yyyymmdstr`。 2. 对同一类型变量使用相似命名,如`getUser()`代替`getUserInfo()`。 3. 变量名应具有描述性,避免使用难以理解的数字,如`MILLISECONDS_IN_A_DAY`代替`86400000`。
17 2
|
2天前
|
JavaScript 前端开发 容器
JavaScript函数学习
JavaScript函数学习
|
3天前
|
JavaScript 前端开发 Java
【JavaScript】ECMAS6(ES6)新特性概览(二):解构赋值、扩展与收集、class类全面解析
【JavaScript】ECMAS6(ES6)新特性概览(二):解构赋值、扩展与收集、class类全面解析
13 2
|
3天前
|
SQL 自然语言处理 前端开发
【JavaScript】ECMAS6(ES6)新特性概览(一):变量声明let与const、箭头函数、模板字面量全面解析
【JavaScript】ECMAS6(ES6)新特性概览(一):变量声明let与const、箭头函数、模板字面量全面解析
8 2
|
3天前
|
JavaScript 前端开发
JavaScript 数组的函数 map/forEach/reduce/filter
JavaScript 数组的函数 map/forEach/reduce/filter
|
3天前
|
JavaScript 前端开发 索引
JS中的substr()和substring()函数有什么区别
JS中的substr()和substring()函数有什么区别
|
4天前
|
SQL 自然语言处理 JavaScript
【JavaScript】ECMAS6(ES6)新特性概览(一):变量声明let与const、箭头函数、模板字面量全面解析
ES6,作为ECMAScript 2015的简称,标志着JavaScript编程语言的一个重要进化节点。它不是渐进的变化,而是一次飞跃式的更新,为开发者带来了一系列强大的新特性与语法糖,极大提升了代码的简洁性、可读性和运行效率。从新的变量声明方式let与const,到优雅的箭头函数、模板字符串,再到让对象操作更为灵活的解构赋值与增强的对象字面量,ES6的每项改进都旨在让JavaScript适应日益复杂的应用场景,同时保持其作为脚本语言的活力与魅力。本文是深入探索这些核心特性的起点,为你铺开一条通向高效、现代JavaScript编程实践
22 0
|
4天前
|
JavaScript 前端开发 Android开发
kotlin开发 webview如何在收到JS调用后,native返回数据给到JS
这段内容描述了在Hybrid App开发中,使用Kotlin的Compose构建的Web视图(WebView)如何通过JsBridge实现JavaScript与原生代码的交互
|
5天前
|
自然语言处理 JavaScript 前端开发
在JavaScript中,this关键字的行为可能会因函数的调用方式而异
【6月更文挑战第15天】JavaScript的`this`根据调用方式变化:非严格模式下直接调用时指向全局对象(浏览器为window),严格模式下为undefined。作为对象方法时,`this`指对象本身。用`new`调用构造函数时,`this`指新实例。`call`,`apply`,`bind`可显式设定`this`值。箭头函数和绑定方法有助于管理复杂场景中的`this`行为。
36 3