原型与原型链

  • 一直搞不懂真的头大

image.png

image.png

一.先去看下什么是构造函数

constructor是原型prototype的一个属性,当函数被定义时候,js引擎会为函数添加原型prototype,并且这个prototype中constructor属性指向函数引用

ps:

因此重写prototype会丢失原来的constructor。 下面会说到这个点,
为了规范开发,在重写对象原型时一般都需要重新给 constructor 赋值,以保证对象实例的类型不被篡改。

二.什么是原型 什么是原型链

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!-- Object.getPrototypeOf() 方法返回指定对象的原型 ( 即, 内部[[Prototype]]属性)。 -->
    <script>
        let arr = ['abc']
        console.log(arr.concat('123'))//0: "abc" 1: "123"

        let hh = {}
        let gg = {}
        // __proto__  理解为 父亲
        console.log(Object.getPrototypeOf(hh) == Object.getPrototypeOf(gg))// true


        let bb = { name :"宝宝" };
        console.log(bb); //是有proto父亲的 
        console.log(bb.hasOwnProperty("name"));//true
        
        // hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。

        // Object.create(proto,[propertiesObject])
        //proto 新创建对象的原型对象。 	创建对象的原型,表示要继承的对象

        // propertiesObject(可选 )	也是一个对象,用于对新创建的对象进行初始化


        //没有原型的对象  叫做完全数据字典对象
        let cc = Object.create(null, {
            name: {
                value: "lalala"
            }
        })


        console.log(cc)//没有__proto__的

        console.log(cc.hasOwnProperty("name"))//cc.hasOwnProperty is not a function

    </script>
</body>

</html>


 

proto 是一个对象 是找长辈里面属性的连接点 长辈里面内置有很多的属性 可以继承使用

在原型上面添加方法


  let smailDog = {
            bigDog()  {
                console.log('多毛大胖狗')
            },
            xiaoDog(){
                console.log('拉布拉多小狗')
            }
        } 
        console.log(smailDog)

        // bigDog: ƒ bigDog()   xiaoDog: ƒ xiaoDog()


        smailDog.__proto__.xiaoDog = function (){
            console.log("小明")
        }
        
        smailDog.xiaoDog(); //输出的是 拉布拉多小狗  
        //自己有 就不用找父级的了  
 

image.png

函数有多个长辈

image.png

image.png
构造函数有prototype 属性

image.png
实例是没有prototype属性的

构造函数 有两个长辈 一个是prototype 另一个是proto 但是他们的使用场景是不一样的

image.png

image.png

User 跟实例 newUser 都是指向同一原型 所以实例是可以使用show 方法

image.png

newUser 实例去找原型 只能通过__proto__ 因为它没有prototype

ps:

这两个长辈是不一样的 他们两个长辈记住也是对象

image.png

image.png

prototype与proto服务场景:(他们两个都是原型 )

  1. prototype 一般是服务于 实例化对象的
  2. 而 proto 一般是服务于 构造函数本身的

Object 构造函数

打印:

image.png

 console.dir(Object)

    let xiaoMing  = new Object();

    Object.prototype.show = function() {
        console.log('峡谷再无小明了')
    }

    xiaoMing.show();//峡谷再无小明了
 

构造函数的实例化对象 可以用到 Object.prototype 里面的方法

image.png

image.png

image.png
在User的父级prototype.–proto–里面找到了show方法
在User的父级–proto–.–proto–里面找到了show方法

说明

console.log(User.prototype.proto == User.proto.proto)//true

可以把__proto__ 理解为查找父级的 连接点

看下Object.prototype

image.png

里面是没有父级的了

console.dir(Object.prototype.proto)//null

null是顶端原型了

image.png

<script>


    console.dir(Object)

    let xiaoMing  = new Object();

    Object.prototype.show = function() {
        console.log('峡谷再无小明了')
    }

    xiaoMing.show();//峡谷再无小明了

    function User() {}

    console.dir(User)

    console.log(User.prototype.__proto__ == User.__proto__.__proto__)//true

    console.dir(Object.prototype)
    console.dir(Object.prototype.__proto__)//null


    let mingQi = new Object();
    mingQi.show();
    User.show();

    console.dir(Object.__proto__)
    console.dir(Object.__proto__.__proto__)//null
</script>
 

构造函数的原型体现

image.png

<code class="hljs language- copyable" lang="">
        let arr = []//new Array
        console.log(Array.prototype)
        console.log(arr.__proto__ == Array.prototype)


        let obj = {}//new Object
        console.log(obj.__proto__ == Object.prototype)

        let bool = true //new Boolean
        console.log(bool.__proto__ == Boolean.prototype)


        let str = ""//new String
        console.log(str.__proto__ == String.prototype)

        String.prototype.show = function() {
            console.log('调用了show方法哦')
        }

        str.show();//调用了show方法哦


    </script>
 

自定义原型设置

<code class="hljs language- copyable" lang="">//  Object.setPrototypeOf(),为现有对象设置原型,返回一个新对象
// 接收两个参数:第一个是现有对象,第二是原型对象。       

let xm = { name :'小明'};
let xmParent = {
    name:'小明爸爸',
    show(){
        console.log('parent method:' + this.name)
    }
}
console.dir(xm)
Object.setPrototypeOf(xm,xmParent)// parent method:小明

xm.show();
xmParent.show();//parent method:小明爸爸 

console.log(Object.getPrototypeOf(xm))// {name: "小明爸爸", show: ƒ}
//Object.getPrototypeOf() 方法返回指定对象的原型 ( 即, 内部[[Prototype]]属性)。
    </script>
 

image.png

原型中constructor的引用

<code class="hljs language- copyable" lang="">        function User(name) {
            this.name = name;

        }
        // User.prototype.show =function(){
        //     console.log('show:'+ this.name)
        // }

        // let lisi = new User.prototype.constructor('李四')// 相当于new Object('李四')

        // console.log(lisi)// User {name: "李四"}
        // lisi.show();//show:李四


        //如果是替换原型 添加多个方法:
        User.prototype = {
            constructor : User,
            show() {
                console.log(this.name)
            },
            view() {
                console.log('User.prototype中的 view method')
            }
        }

        let lisi = new User.prototype.constructor('李四')// 相当于new Object('李四')
        console.log(lisi);
        lisi.show();//lisi.show is not a function
        //为了解决这问题  在上面{}中加上 constructor : User,  输出:李四
    </script>
 

image.png

通过原型找到构造函数 构造函数实例化新对象

自定义方法 创建新对象 new User(‘’) 被自定义函数替换掉

<code class="hljs language-<!DOCTYPE copyable" lang="<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>


    <script>

        // function User(name) {
        //     this.name = name;

        // }
        // let lisi = new User.prototype.constructor('李四')// 相当于new Object('李四')
        // console.log(lisi)//{name: "李四"}



        // function User(name) {
        //     this.name = name,
        //         this.show = function () {
        //             console.log(this.name);
        //         }
        // }

        // let hd = new User('拉不拉多')
        // console.log(hd)//{name: "拉不拉多" show: ƒ ()}

        // // 现在要创建一个新对象  xj = {name: "牛逼" show: ƒ () }


        // function createByObject(obj, ...args) {
        //     //拿到传进来的那个变量的 原型  比如User.prototype 再去找他的构造函数
        //     const constructor = Object.getPrototypeOf(obj).constructor
        //     return new constructor(...args)//new User.prototype.constructor('李四')// 相当于new Object('李四')
        // }
        // let mq = createByObject(hd, '溟七');
        // console.log(mq.name)//溟七
        // mq.show();//溟七




        // 现在要改变原型了 把show方法放到原型上
        function User(name) {
            this.name = name
              
        }

        User.prototype = {
            constructor: User,
            show(){
                console.log(this.name);
            }
        }

         let hd = new User('拉不拉多')
        console.log(hd)//{name: "拉不拉多" show: ƒ ()}

        


        function createByObject(obj, ...args) {
            //拿到传进来的那个变量的 原型  比如User.prototype 再去找他的构造函数
            const constructor = Object.getPrototypeOf(obj).constructor
            return new constructor(...args)//new User.prototype.constructor('李四')// 相当于new Object('李四')
        }
        let mq = createByObject(hd, '溟七');
        console.log(mq.name)//溟七
        mq.show();//溟七

    </script>
</body>

</html>
 

原型继承的体现形式:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>


    <script>
        let a = {name:'a'}
        let c = {name:'c'}
        let b = {name:'b',
        show(){
            console.log(this.name)
        },
        view(){
            console.log('view method')
        }
    
    }

        Object.setPrototypeOf(a,b)
        Object.setPrototypeOf(c,b)
        console.log(a)
        a.show();
        c.show();
        a.view();
        c.view();


    </script>
</body>
</html>

 

image.png

image.png

Object.isPrototypeOf

  • 明确判断对象是否是其他对象原型链上的一份子
 <script>
        // JS isPrototypeOf()方法:检测一个对象是否存在于另一个对象的原型链中
        // 或者说一个对象是否被包含在另一个对象的原型链中

        let a = {}
        let b = {}
        let c = {}
        // console.log(a.isPrototypeOf(b) ) //false  a对象是否是b 对象原型链上的一份子

        // console.log(Object.prototype.isPrototypeOf(a))//true  Object.prototype 是a 的顶层

            Object.setPrototypeOf(b,c)
            Object.setPrototypeOf(a,b)

            console.log(b.isPrototypeOf(a))
            console.log(c.isPrototypeOf(a))
            console.log(c.isPrototypeOf(b))

    </script>
 

image.png

in 与 hasOwnProperty 属性检测差异

in 会攀升原型链去查找
而hasOwnProperty只会去当前对象查找

        let one = {
            name: '换喜临门'
        }

        let tow = {
            url: 'www.abc.com'
        }
        Object.prototype.web = "i love you"

        // "name" 是指key
        // console.log("name" in one)//true
        // console.log("url" in one)//false

        Object.setPrototypeOf(one, tow)

        console.log("url" in one)//true

        console.log("web" in one)//true
        console.log("web" in tow)//true


        console.log(one.hasOwnProperty('name'))//true  属性是否在自己身上
        console.log(one.hasOwnProperty('url'))//false
        console.log(one.hasOwnProperty('web'))//false

        // for(const key in one){
        //     console.log(key)//name url web
        // }



        for (const key in one) {
            // console.log(key)// 继承name url web
            if (one.hasOwnProperty(key))
            console.log(key) //不继承name
        }

 

sort()方法

image.png

image.png

image.png

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- sort() 方法用于对数组的元素进行排序,并返回数组 -->
<script>
//1.数字排序
// let arr = [2, 1, 3, 5, 4];
// arr = arr.sort((n1, n2) => {
//     // return -1; //返回负值  交换顺序
//     // return 0 或者 1   //返回正值   保持顺序不变
//     console.log(n1, n2);
//     return n2 - n1;
//     // n2 - n1  从大到小
//     // n1 - n2  从小到大
// });
// console.log(arr);//[5, 4, 3, 2, 1]
// ps:不传参数,将不会按照数值大小排序,按照字符编码的顺序进行排序;
// var arr = ['General','Tom','Bob','John','Army'];
// var resArr = arr.sort();
// console.log(resArr);//输出   ["Army", "Bob", "General", "John", "Tom"]
// var arr2 = [30,10,111,35,1899,50,45];
// var resArr2 = arr2.sort();
// console.log(resArr2);//输出   [10, 111, 1899, 30, 35, 45, 50]
// sort默认的排序方式为字母排序,
// 2.字母排序(sort默认排序)
var arr = ["za", "zb", "a", "b", "xc", "xa"];
arr.sort();
console.log(arr);
// 运行结果:["a", "b", "xa", "xc", "za", "zb"]
// 3.对象属性排序  面试很常见的   根据数组中的对象的某个属性值排序
var obj = [
{ name: "lucy", num: 400 },
{ name: "nancy", num: 110 },
{ name: "maria", num: 200 },
{ name: "sb" },
{ name: "zhu" },
{ name: "dog", num: 'abc' }
];
obj.sort(compare("num"));
console.log(obj);
// 0: {name: "nancy", num: 110}
// 1: {name: "maria", num: 200}
// 2: {name: "lucy", num: 400}
// 3: {name: "sb"}
// 4: { name: "zhu" }
// 5: { name: "dog", num: "abc" }
// compare()中参数必须是这个对象的属性名称,而你要比较的这些对象里面,应该要有这个属性名称,否则会出错
//数组对象属性值排序  
function compare(property) {
return function (a, b) {
//value1 - value2升序
//value2 - value1降序
var value1 = a[property];
var value2 = b[property];
return value1 - value2;//升序 小到大
}
}
var arr5 = [{ id: 10 }, { id: 5 }, { id: 6 }, { id: 9 }, { id: 2 }, { id: 3 }];
arr5.sort(function (a, b) {// 
return a.id - b.id
})
console.log(arr5);
//输出新的排序
//		{id: 2}
//		{id: 3}
//		{id: 5}
//		{id: 6}
//		{id: 9}
//		{id: 10}
// 4.根据数组中的对象的多个属性值排序,多条件排序;
var arr6 = [{ id: 10, age: 2 }, { id: 5, age: 4 }, { id: 6, age: 10 }, { id: 9, age: 6 }, { id: 2, age: 8 }, { id: 10, age: 9 }];
arr6.sort(function (a, b) {
if (a.id === b.id) {//如果id相同,按照age的降序
return b.age - a.age
} else {
return a.id - b.id
}
})
console.log(arr6);
//输出新的排序
//		{id: 2, age: 8}
//		{id: 5, age: 4}
//		{id: 6, age: 10}
//		{id: 9, age: 6}
//		{id: 10, age: 9}
//		{id: 10, age: 2}
// 面试题:
let arrDiy = [3, 15, 8, 29, 102, 22]
console.log(arrDiy.sort())
// 0: 102
// 1: 15
// 2: 22
// 3: 29
// 4: 3
// 5: 8
// 按照unicode编码进行排序的  会先进行toString()方法
// ["3", "15", "8", "29", "102", "22"]
// 第一次排序结果: ["15","102","29","22","3","8"]
// 第2次排序结果: ["102","15","22","29","3","8"]
// 数字 => 大写字母 => 小写字母 => “中文”
var newArr = [3, 15, 8, 29, 102, 22]
newArr.sort(function (a, b) {
// console.log('排序:' + a +"," + b) //两个两个进行对比
console.log(a + "-" + b + "=" + (a - b)) //看表达式 像第一个减第二个 但是结果不是
// 15 - 3 =12      3 15
//  8 - 15=-7      8 15 
//  29 - 8=21      8 29 
// 102 - 29=73      29  102
//  22 - 102=-80    22 102
//规律 后面的数值和前面的数字相减
return a - b
})
console.log(newArr)
// [3, 8, 15, 22, 29, 102]
//底层算法:
</script>
</body>
</html>

借用方法

打个比方说: 你家没车 你爸爸妈妈也没买车 你爷爷奶奶也没车 你邻居是我 我有奔驰 你可以借用我的车去开

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//你家没车 你的邻居有车 你要借用人家的车
var Car = function () {
this.name = "车";
}
var benz = new Car();
console.log(benz.name);//车 
console.log(benz)
//在原型上面加对象
Object.setPrototypeOf(benz, { name: "小轿车", price: "23456" }); //__proto__ :{name:"小轿车",price:"23456"}
console.log(benz.name);  //车
console.log(benz.price);//23456
//在原型上面加方法
let hh = {
data: [12, 50, 60, 42, 10, 5]
}
Object.setPrototypeOf(hh, {//在原型上添加方法
max() {
return this.data.sort((x, y) => y - x)[0];
}
})
console.log(hh.max())//60
let xj = {
lessons:{js:87,php:100,node:99,linux:45},
// get date(){//why 什么要加get?
// get date(){//why 什么要加get?  方法要同名
get data(){//why 什么要加get?
return Object.values(this.lessons) 
}
}
console.log(hh.max.apply(xj))//xj中没有data属性  就读取不到  那么就在xj对象中加上一个方法返回 成绩的分数
</script>
</body>
</html>

call 与apply 传递的参数 可用扩展运算符转换

let mq = {
data:[1,2,336,445,52,12,120]
}
console.log(Math.max(mq.data))//NaN
console.log(Math.max.apply(null,mq.data))//445
//如果用call 就要展开
console.log(Math.max.call(null,...mq.data))//445
let xm = {
lessons:{
math:100,english:0,huaxue:20,shengwu:95
}
}
console.log(Math.max.apply(null,Object.values(xm.lessons)))//100
console.log(Math.max.call(null,...Object.values(xm.lessons)))//100

改变构造函数原型不是继承

image.png

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function Menber(){
}
function Admin(){
}
function User(){
}
User.prototype = {
constructor:User,
name:function(){
console.log('user.name');
}
}
let a = new User();
a.name()
Admin.prototype = User.prototype;
Admin.prototype.role = function() {
console.log('admin.role');
}
Menber.prototype = User.prototype;
Menber.prototype.role = function() {
console.log('Menber.role');
}
let b = new Admin();
b.role();//Menber.role  会被覆盖
let c = new Menber();
c.role();//Menber.role  
</script>
</body>
</html>

image.png

继承是原型的继承

image.png

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function Menber(){
}
function Admin(){
}
function User(){
}
User.prototype = {
constructor:User,
name:function(){
console.log('user.name');
}
}
let a = new User();
a.name()
// Admin.prototype.__proto__= User.prototype;
// Admin.prototype.role = function() {
//     console.log('admin.role');
// }
//放在定义之前b.role is not a function
Admin.prototype = Object.create(User.prototype)
Admin.prototype.role = function() {
console.log('admin.role');
}
Menber.prototype.__proto__= User.prototype;
Menber.prototype.role = function() {
console.log('Menber.role');
}
let b = new Admin();
b.role();//admin.role
let c = new Menber();
c.role();//Menber.role  
</script>
</body>
</html>

多继承的问题

image.png

Admin 实例想用credit 和request 原型里面的方法

member实例想用credit 和request 原型加上 member里面的方法

但是 Admin是不需要用到member原型上的方法的

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 封装好的方法
function inherit(child, father) {
child.prototype = Object.create(father.prototype)
// 设置constructor 不可以遍历
Object.defineProperty(child.prototype, 'constructor', {
value: child,
enumerable: false
})
}
function Credit() { }
inherit(Credit, Address)
Credit.prototype.total = function () {
console.log('统计积分');
}
function Request() { }
//注意:继承要写在原型之前
inherit(Request, Credit)
Request.prototype.ajax = function () {
console.log("请求后台");
}
function Address() {
}
//ps :这里是要实现 Credit 继承Address  所以在Credit.prototype 原型改变之前就要实现继承 所以是加到上面的位置  而不是这里
// inherit(Credit, Address)
Address.prototype.getAddress = function () {
console.log('获取地址');
}
//上面的都要写在user之前
function User(name, age) {
this.name = name;
this.age = age;
}
inherit(User, Request);
User.prototype.show = function () {
console.log('你好呀,陆明清');
}
function Admin(name, age) {
//初始化实例数据
User.call(this, name, age);
}
inherit(Admin, User)
// function Address(){
// }
// inherit(Credit,Address)
// Address.prototype.getAddress = function(){
//     console.log('获取地址');
// }
let newAdmin = new Admin("京动", 123);
console.log(newAdmin);// {name: "京动", age: 123}
newAdmin.ajax();//请求后台
newAdmin.total();//统计积分
//getAddress是Admin实例不用的
newAdmin.getAddress();//获取地址
function Member(name, age) {
//初始化实例数据
User.call(this, name, age);
}
inherit(Member, User)
let newMember = new Member('啦啦', 12);
console.log(newMember);
newMember.getAddress();
newMember.ajax();
newMember.total();
</script>
</body>
</html>

使用mixin (混合)实现多继承

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 封装好的方法
function inherit(child, father) {
child.prototype = Object.create(father.prototype)
// 设置constructor 不可以遍历
Object.defineProperty(child.prototype, 'constructor', {
value: child,
enumerable: false
})
}
const Credit = {
total() {
console.log('统计积分');
}
}
const Request = {
ajax () {
console.log("请求后台");
}
}
const Address = {
getAddress(){
console.log('获取地址');
}
}
//上面的都要写在user之前
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function () {
console.log('你好呀,陆明清');
}
function Admin(name, age) {
//初始化实例数据
User.call(this, name, age);
}
inherit(Admin, User)
//把对象压到原型上
Admin.prototype = Object.assign(Admin.prototype, Credit, Request)
// console.log(Admin.prototype);//{total: ƒ, ajax: ƒ, constructor: ƒ}
let newAdmin = new Admin("京动", 123);
console.log(newAdmin);// {name: "京动", age: 123}
newAdmin.ajax();//请求后台
newAdmin.total();//统计积分
// newAdmin.getAddress();//用不了的
function Member(name, age) {
//初始化实例数据
User.call(this, name, age);
}
inherit(Member, User)
Member.prototype = Object.assign(Member.prototype,Credit,Request,Address)
let newMember = new Member('啦啦', 12);
console.log(newMember);
newMember.getAddress();
newMember.ajax();
newMember.total();
newMember.show();//你好呀,陆明清
</script>
</body>
</html>

mixin的内部继承与super关键字

image.png

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 封装好的方法
function inherit(child, father) {
child.prototype = Object.create(father.prototype)
// 设置constructor 不可以遍历
Object.defineProperty(child.prototype, 'constructor', {
value: child,
enumerable: false
})
}
const Request = {
ajax () {
return "请求后台";
}
}
// Credit 默认原型是指向 Object.prototype
const Credit = {
__proto__:Request,
//需求就是Credit 里面也要用到ajax()
total() {
// console.log(this);//Admin {name: "京动", age: 123}  实例调用.totol() 其实它会先找到原型Admin.prototype  里面的total  实质上这个this是指 是Credit构造函数这个对象   它的原型上Request 才有ajax()
console.log(this.__proto__.ajax() + '统计积分');
// console.log(super.ajax() + '统计积分');
}
}
const Address = {
getAddress(){
console.log('获取地址');
}
}
//上面的都要写在user之前
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function () {
console.log('你好呀,陆明清');
}
function Admin(name, age) {
//初始化实例数据
User.call(this, name, age);
}
inherit(Admin, User)
//把对象压到原型上
Admin.prototype = Object.assign(Admin.prototype, Credit, Request)
// console.log(Admin.prototype);//{total: ƒ, ajax: ƒ, constructor: ƒ}
let newAdmin = new Admin("京动", 123);
console.log(newAdmin.ajax());;//请求后台
newAdmin.total();//请求后台统计积分
</script>
</body>
</html>

image.png

image.png

原创文章,作者:我心飞翔,如若转载,请注明出处:https://www.pipipi.net/14788.html

发表评论

登录后才能评论