JavaScript 进阶征途:解锁Function奥秘,深掘Object方法精髓


在这里插入图片描述



个人主页:学习前端的小z

个人专栏:JavaScript 精粹

本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结,欢迎大家在评论区交流讨论!
在这里插入图片描述

文章目录

  • 🈵Function方法 与 函数式编程
    • 💝1 call
    • 💝2 apply
    • 💝3 柯理化函数(currying)
    • 💝4 bind
    • 💝5 偏函数 (partial)
    • 💝6 通道函数(compose)
      • 🏆6.1 柯理化与偏函数区别
  • 🈵Object 方法 深入
    • 💝1 Object.defineProperty()
    • 💝2 Object.entries()
    • 💝3 Object.keys()
    • 💝4 Object.fromEntries()
    • 💝5 Object.preventExtensions()


在这里插入图片描述


🈵Function方法 与 函数式编程

💝1 call

语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]])

定义:调用一个对象的一个方法,以另一个对象替换当前对象。

说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。

如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。

    let myName = 'goudan';
    let myAge = 13;     
    function showMsg(msg){
        return (msg + '').toLowerCase();
    }

    showName(myName); // 'goudan'

这段代码很容易就能看懂,在实际开发工作中,我们会处理不同的数据集合,这时候声明单一变量已经无法满足胃口,需要通过json的形式来存储数据

    let person = {
            name:"kyogre",
            age:13,
            hobby:"coding"
            }

    let newPerson  ={
            name:'dachui',
            age:99,
            hobby:'eat'
            }
    function showMsg(msg){
        return  (msg + '').toLowerCase();
    }

    showMsg(person.name) // 'kyogre'
    showMsg(newPerson.name) // 'dachui'

存储数据的方式发生了改变,但是我们处理数据的方式还是那么。。。古老,如果业务复杂一点

    function format(msg){
            return msg+''.toLowerCase();
    }
    function show(msg){
             return '信息的内容是:'+ format(msg);
    }
    show(person.name) // '信息内容是:kyogre'
    show(newPerson.name) // '信息内容是:dachui'

显示的传递上下文对象 (穿参)使得业务越来越复杂,这种叠加会让开发变得冗余和难以解读,bug和闭包横飞

那我们看看通过this如何清晰解决问题

通常this不会指向函数自身,而是调用函数的对象主体。当然,我们可以强制让function自身成为对象主体,这个以后咱们讨论; json本身就是对象,我们是否可以这样:

    const person = {
            name:"kyogre",
            age:13,
            hobby:"coding"
            }
    const newPerson  ={
        name:'dachui',
        age:99,
        hobby:'eat'
        }

    function format(){
            return this.name+''.toLowerCase();

    }

问题来了,不让穿参这个format中的this指向谁呢? 指向调用format的对象本身,可是调用主体并不明确,可能是person也可能是newPerson,这时回过头看看call的定义吧:调用一个对象的一个方法,以另一个对象替换当前对象。 将函数内部执行上下文对象由原始对象替换为指定对象

        const name = '我叫window';    //全局变量 非严格模式下都为 window的属性   window.name;
        function format(){
            return  (this.name + '').toLowerCase();
        }
        format(); //'我叫window';     

不要惊慌,本身他就是这样window会作为调用顶级作用域链函数的对象主体;这里的this默认为 window, 用person对象代替window这个默认this主体去执行format会怎么样呢

        format.call(person); 
        // kyogre 
        format.call(newPerson);
        // dachui
        function show(){
             return '信息的内容是:'+ format.call(this);
        }


        show.call(person); // 信息的内容是:kyogre

感觉自己了解了this和call的小明,已经肆无忌惮的笑了起来,这样他就可以从繁重的回调与参数传递中解脱了,并且能够实现方法的初级模块化。


下面可以用call做一些平常的操作

    function isArray(object){
        return Object.prototype.toString.call(object) == '[object Array]';
    }// 借用Object原型上的toString方法来验证下对象是否是数组?

    function accumulation(){
        return [].reduce.call(arguments,(a,b)=>{return a+b}
    }//让不能使用数组方法的arguments类数组集合使用借用数组的reduce方法

    return Array.prototype.forEach.call($$('*'),(item)=>{item.style.border = '1px solid red';}
  

💝2 apply

语法:func.apply(thisArg, [argsArray])

call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组

const person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
const person1 = {
  firstName:"John",
  lastName: "Doe"
}
person.fullName.apply(person1, ["Oslo", "Norway"]);
Math.max.apply(null, [1,2,3]); // 3

const arr = [1,2,3];
const otherArr = [3,4,5];
arr.push.apply(arr,otherArr);
console.log(arr); // [1, 2, 3, 3, 4, 5]

💝3 柯理化函数(currying)

在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

  function currying(fn) {
      const args = Array.prototype.slice.call(arguments, 1);

      const inlay = function () {
        if (arguments.length === 0) {
          return fn.apply(this, args);
        }
        if(arguments.length > 0 ){
            Array.prototype.push.apply(args, arguments);
            return inlay;
        }     
      }
      return inlay;
    }

    function add() {
      const vals = Array.prototype.slice.call(arguments);
      return vals.reduce((pre, val) => {
        return pre + val;
      });
    }

    let newAdd = currying(add, 1, 2, 3);
    newAdd(4, 5);
    newAdd(6, 7)(6)(2, 3);
    console.log(newAdd()); //39

💝4 bind

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

语法:

function.bind(thisArg[, arg1[, arg2[, ...]]])

参数:

  • thisArg
    • 调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用[new](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/new)运算符构造绑定函数,则忽略该值。当使用 bindsetTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object。如果 bind 函数的参数列表为空,或者thisArgnullundefined,执行作用域的 this将被视为新函数的thisArg
  • arg1, arg2, …
    • 当目标函数被调用时,被预置入绑定函数的参数列表中的参数。

返回值:

返回一个原函数的拷贝,并拥有指定的 **this** 值和初始参数。

const OBJ = {
    petName: 'kyogre',
    qq: '2971411050',
    sayHi: function () {
        console.log(`我是${this.petName} 很高兴认识你`)
    }
}

let sayHi = OBJ.sayHi;
sayHi(); //我是undifined 很高兴认识你  ps: this非严格模式指向了window window.petName不存在


let sayHi = OBJ.sayHi.bind(OBJ);
sayHi(); //我是kyogre 很高兴认识你 ps:通过bind强制绑定sayHi函数内部的this指向OBJ

💝5 偏函数 (partial)

在计算机科学中,局部应用(偏函数)是指固定一个函数的一些参数,然后产生另一个更小元的函数。(什么是元?元是指函数参数的个数,比如一个带有两个参数的函数被称为二元函数。)

bind() 的另一个最简单的用法是使一个函数拥有预设的初始参数。只要将这些参数(如果有的话)作为 bind() 的参数写在 this 后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。

function list() {
  return Array.prototype.slice.call(arguments);
}

function addArguments(arg1, arg2) {
    return arg1 + arg2
}

const list1 = list(1, 2, 3); // [1, 2, 3]

const result1 = addArguments(1, 2); // 3

// 创建一个函数,它拥有预设参数列表。
const leadingThirtysevenList = list.bind(null, 37);

// 创建一个函数,它拥有预设的第一个参数
const addThirtySeven = addArguments.bind(null, 37); 

const list2 = leadingThirtysevenList(); 
// [37]

const list3 = leadingThirtysevenList(1, 2, 3); 
// [37, 1, 2, 3]

const result2 = addThirtySeven(5); 
// 37 + 5 = 42 

const result3 = addThirtySeven(5, 10);
// 37 + 5 = 42 ,第二个参数被忽略





💝6 通道函数(compose)

按照顺序

  function toUpperCase(str){
     return str.toUpperCase(); // 将字符串变成大写
 }
 
 function add(str){
     return str + '!!!'; // 将字符串拼接
 }
 
 function split(str){
     return str.split(''); // 将字符串拆分为数组
 }
 
 function reverse(arr){
     return arr.reverse(); // 将数组逆序
 }
 
 function join(arr){
     return arr.join('-'); // 将数组按'-'拼接成字符串
 }


  function compose(){
      const args = Array.prototype.slice.call(arguments); // 转换为数组使用下面的方法
      return function(x){
          return args.reduceRight(function(result, cb){
              return cb(result);
         }, x);
      }
  }
  
 const f = compose(add, join, reverse, split, toUpperCase);
 console.log( f('cba') ); // A-B-C!!!

🏆6.1 柯理化与偏函数区别

  • 柯里化是将一个多参数函数转换成多个单参数函数,也就是将一个 n 元函数转换成 n 个一元函数。

  • 局部应用则是固定一个函数的一个或者多个参数,也就是将一个 n 元函数转换成一个 n - x 元函数。


在这里插入图片描述


🈵Object 方法 深入

Object.getOwnPropertyNames()
//方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。

Object.getPrototypeOf() 
//方法返回指定对象的原型(内部[[Prototype]]属性的值)。

Object.getOwnPropertyDescriptors() 
//方法用来获取一个对象的所有自身属性的描述符。

Object.getOwnPropertyDescriptor()
//方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

Object.assign() 
//方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

Object.create()
//方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 (请打开浏览器控制台以查看运行结果。)

Object.freeze() 
//方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

Object.isFrozen()
//方法判断一个对象是否被冻结。

Object.isSealed() 
//方法判断一个对象是否被密封。

hasOwnProperty() 
//方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。

isPrototypeOf() 
//方法用于测试一个对象是否存在于另一个对象的原型链上。

Object.is() 
//方法判断两个值是否为同一个值。

💝1 Object.defineProperty()

方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

语法

Object.defineProperty(obj, prop, descriptor)

参数

  • obj

    要定义属性的对象。

  • prop

    要定义或修改的属性的名称或 Symbol

  • descriptor

    要定义或修改的属性描述符。

返回值

被传递给函数的对象。

该方法允许精确地添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的,在枚举对象属性时会被枚举到(for...in 或 Object.keys 方法),可以改变这些属性的值,也可以删除这些属性。这个方法允许修改默认的额外选项(或配置)。默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改(immutable)的。

对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。一个描述符只能是这两者其中之一;不能同时是两者。

这两种描述符都是对象。它们共享以下可选键值(默认值是指在使用 Object.defineProperty() 定义属性时的默认值):

  • configurable

    当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。 默认为 false

  • enumerable

    当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。 默认为 false

数据描述符还具有以下可选键值:

  • value

    该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。 默认为 undefined

  • writable

    当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符改变。 默认为 false

存取描述符还具有以下可选键值:

  • get

    属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。 默认为 undefined

  • set

    属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。 默认为 undefined

描述符默认值汇总

  • 拥有布尔值的键 configurableenumerablewritable 的默认值都是 false
  • 属性值和函数的键 valuegetset 字段的默认值为 undefined

描述符可拥有的键值

configurableenumerablevaluewritablegetset
数据描述符可以可以可以可以不可以不可以
存取描述符可以可以不可以不可以可以可以

如果一个描述符不具有 valuewritablegetset 中的任意一个键,那么它将被认为是一个数据描述符。如果一个描述符同时拥有 valuewritablegetset 键,则会产生一个异常。

记住,这些选项不一定是自身属性,也要考虑继承来的属性。为了确认保留这些默认值,在设置之前,可能要冻结 Object.prototype,明确指定所有的选项,或者通过 Object.create(null)__proto__ 属性指向 null

Object.defineProperties() 可以对多条属性进行修改

var obj = {};
Object.defineProperties(obj, {
  'property1': {
    value: true,
    writable: true
  },
  'property2': {
    value: 'Hello',
    writable: false
  }
  // etc. etc.
});

💝2 Object.entries()

方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。

语法

Object.entries(obj)

参数

  • obj

    可以返回其可枚举属性的键值对的对象。

返回值

​ 给定对象自身可枚举属性的键值对数组。

const object1 = {
  a: 'somestring',
  b: 42
};

for (const [key, value] of Object.entries(object1)) {
  console.log(`${key}: ${value}`);
}

// expected output:
// "a: somestring"
// "b: 42"
// order is not guaranteed

💝3 Object.keys()

方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。。

语法

Object.keys(obj)

参数

  • obj

    要返回其枚举自身属性的对象。

返回值

​ 一个表示给定对象的所有可枚举属性的字符串数组。

const arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']

// array like object
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

💝4 Object.fromEntries()

方法把键值对列表转换为一个对象。

语法

Object.fromEntries(iterable);

参数

  • iterable

    类似 ArrayMap 或者其它实现了可迭代协议的可迭代对象。

返回值

一个由该迭代对象条目提供对应属性的新对象

const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 42 }

💝5 Object.preventExtensions()

方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。

语法

Object.preventExtensions(obj)

参数

  • obj

    将要变得不可扩展的对象。

返回值

已经不可扩展的对象。

var obj = {};
var obj2 = Object.preventExtensions(obj);
obj === obj2;  // true
 
// 字面量方式定义的对象默认是可扩展的.
var empty = {};
Object.isExtensible(empty) //=== true
 
// ...但可以改变.
Object.preventExtensions(empty);
Object.isExtensible(empty) //=== false
 
// 使用Object.defineProperty方法为一个不可扩展的对象添加新属性会抛出异常.
var nonExtensible = { removable: true };
Object.preventExtensions(nonExtensible);
Object.defineProperty(nonExtensible, "new", { value: 8675309 }); // 抛出TypeError异常
 
// 在严格模式中,为一个不可扩展对象的新属性赋值会抛出TypeError异常.
function fail()
{
  "use strict";
  nonExtensible.newProperty = "FAIL"; // throws a TypeError
}
fail();const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 42 }

在这里插入图片描述


参考 : http://bclary.com/log/2004/11/07/#a-11.9.3

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/608837.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux|进程控制

进程创建 fork函数初识 在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。 返回值:子进程中返回0,父进程返回子进程id,出错返回-1 进程调用fork,当…

ICode国际青少年编程竞赛- Python-2级训练场-数独

ICode国际青少年编程竞赛- Python-2级训练场-数独 1、 Spaceship.step(3)2、 Spaceship.step(3)3、 Spaceship.step(1) Spaceship.turnLeft() Spaceship.step(1)4、 Spaceship.step(3) Spaceship.turnRight() Spaceship.step(1)5、 Spaceship.step(4) for i in range(3):Spaces…

企业级通用业务 Header 处理方案

目录 01: 处理 PC 端基础架构 02: 通用组件:search 搜索框能力分析 03: 通用组件:search 搜索框样式处理 04: 通用组件:Button 按钮能力分析 05: 通用组件:Button 按钮功能实现 06: 通用组件:完善 search 基本…

MySQL学习笔记11——数据备份 范式 ER模型

数据备份 & 范式 & ER模型 一、数据备份1、如何进行数据备份(1)备份数据库中的表(2)备份数据库(3)备份整个数据库服务器 2、如何进行数据恢复3、如何导出和导入表里的数据(1&#xff09…

ARP命令

按照缺省设置,ARP高速缓存中的项目是动态的,每当发送以恶个指定的数据报且高速缓存中不存在当前项目时,ARP便会自动添加该项目。一旦高速缓存的项目被输入,就已经开始走向失效状态。因此,如果ARP高速缓存中的项目很少或…

擎天科技与禅道合作,打造统一的项目管理平台

统一、全面的项目管理平台能够帮助企业优化管理流程,提升业务效率。擎天集团选择与禅道软件合作,打造统一的项目管理平台,在降低自研软件的研发成本、打破团队信息孤岛、保障数据全面性等方面效果显著,大大提高了团队沟通协作效率…

如何使用 ArcGIS Pro 计算容积率

容积率是指地上建筑物的总面积与用地面积的比率,数值越小越舒适,这里为大家介绍一下如何使用ArcGIS Pro 计算容积率,希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的建筑和小区数据,除了建筑和小区数据&am…

verilog中不重叠序列检测

编写一个序列检测模块,检测输入信号(a)是否满足011100序列, 要求以每六个输入为一组,不检测重复序列,例如第一位数据不符合,则不考虑后五位。一直到第七位数据即下一组信号的第一位开始检测。当…

AngularJS基本概念

版本: AngularJs 1.x:https://angularjs.org/ AngularJs 2:https://angular.io/ 或 https://angular.cn/ 实现语言: Angular 1.x:使用ES(avaScript)编写,可直接在浏览器中运行。 Angular 2&#xff1a…

Electron-Vue 脚手架避坑实录,兼容Win11,升级electron22,清理控制台错误

去年的还是有用的,大家继续看,今年再补充一些Electron-Vue 异常处理方案 M1 和 Window10_electron异常处理-CSDN博客 代码gitee.com地址 electron-demo: electron 22 初始代码开发和讲解 升级electron为22版本(这个版本承上启下&#xff0c…

内网穿透速度慢

内网穿透速度慢原因及优化策略 在计算机网络应用中,内网穿透是一个常见的需求,它允许外部网络访问位于内部网络(如企业局域网或家庭网络)中的设备或服务。然而,有时用户在进行内网穿透时会遇到速度慢的问题&#xff0…

【二次元MMORPG游戏开发】任务系统技术拆解

引言 各位同学大家好。在今天的分享当中,我将对任务系统去做一个拆解。也许你见过很多任务系统,但是今天我要分享的是我们经过一个框架迭代以后的任务系统。我会结合客户端的功能演示给大家去讲解。 跟着演示学开发 基本操作 好,首先我们点…

C++ | Leetcode C++题解之第80题删除有序数组中的重复项II

题目&#xff1a; 题解&#xff1a; class Solution { public:int removeDuplicates(vector<int>& nums) {int n nums.size();if (n < 2) {return n;}int slow 2, fast 2;while (fast < n) {if (nums[slow - 2] ! nums[fast]) {nums[slow] nums[fast];slo…

【Linux】项目自动化构建工具make/makefile

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; Linux &#x1f389;其它专栏&#xff1a; C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解Linux中项目自动化构建工具make/makefile的相关内容。 如果看到最后…

[windows系统安装/重装系统][step-2]BIOS设置UEFI引导、磁盘分区GPT分区、安装系统[含完整操作拍照图片]

背景 先准备U盘启动盘和系统镜像: [windows系统安装/重装系统][step-1]U盘启动盘制作&#xff0c;微软官方纯净系统镜像下载 前言&#xff08;略长&#xff0c;建议可跳过&#xff09; 我的笔记本升级了CPU升级了内存后出现了一个小问题&#xff0c; 每次启动徽标显示后会…

hyper-v启动centos7虚拟机不能联网

虚拟网卡要和之前虚拟机里面设置的GATEWAY一致。 安装CentOS遇到Error setting up base repository换url 或者换镜像包iso(这个有用&#xff09; 没掌握摸Yu的精髓 好累啊

安全加固

目录 1.文件锁定管理 2.设置用户账户有效期 3.查看并清除命令历史记录 4.设置用户超时登出时间 5.用户切换 6.用户提权 7.禁用重启热键CtrlAltDel 8.设置单用户模式密码 9.调整BIOS引导设置 10.禁止root用户从本地登录&#xff1a; 11.禁止root用户通过ss…

【算法刨析】完全背包

完全背包与01背包的区别 01背包对于一个物品只能选择一次&#xff0c;但是完全背包可以选择任意次&#xff1b; 思路 和01背包类似&#xff0c;01背包我们只需要判断选或不选&#xff0c;完全背包也是如此&#xff0c;不同的是&#xff0c;对于这个物品我们在判断选后在增加一…

【考试100】2023年监理《目标控制(土建)》真题及答案精选

​来源&#xff1a;考试100 一、单项选择题 1、工程建设与使用中&#xff0c;保证人身和环境免受危害&#xff0c;是建设工程质量特性中的&#xff08; &#xff09;要求。A .适用性 B .耐久性 C .安全性 D .可靠性 参考答案&#xff1a;C 解析&#xff1a;安全性&…

2024 全自动ai生成视频MoneyPrinterTurbo源码

只需提供一个视频 主题 或 关键词 &#xff0c;就可以全自动生成视频文案、视频素材、视频字幕、视频背景音乐&#xff0c;然后合成一个高清的短视频。 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/89208288 更多资源下载&#xff1a;关注我。
最新文章