关于IIFE

IIFE是JS中的一个概念,就是立即调用函数表达式。

为什么需要IIFE

我们先用一个简单的例子说一下为什么需要这个IIFE。

新建一个窗口,插入一个WebViewer,打开网页。

插入一个按钮,按钮代码为:

'''Async
Dim
js As String = <![CDATA[
    const a = 100;
    const b = 200;
    const sum = a + b;
    sum;
]]>
.Value
Dim
wv As WebViewer = e.Form.Controls("WebViewer1").WebViewer
Dim
sum As Double = Await wv.ExecuteScriptAsync(js)
MessageBoxA.Show(Sum)

第一次运行会显示计算结果300,第二次运行就会出错,这是因为:

第一次执行时,JS代码在全局作用域中创建了三个变量:a、b、sum。这些变量会一直存在于WebViewer的全局环境中,第二次执行同样的代码时,JS引擎会发现 const a 已经定义过了,于是抛出错误:Identifier 'a' has already been declared(标识符'a'已被声明)。这就是全局变量污染的问题——每次执行的代码都在同一个全局作用域中运行,变量会相互干扰。

当然这里的问题很好解决,不用const,改用var即可。

但除了全局变量污染问题,还可能存在变量覆盖、命名冲突、并行执行、内存泄漏等多种问题。

所以对于Foxtable执行的脚本,建议都封装成IIFE执行,以上面的代码为例:

'''Async
Dim
js As String = <![CDATA[
    (function() {
        const a = 100;
        const b = 200;
        return a + b;
    })();
]]>
.Value
Dim
wv As WebViewer = e.Form.Controls("WebViewer1").WebViewer
Dim
sum As Double = Await wv.ExecuteScriptAsync(js)
MessageBoxA.Show(sum)

你现在反复单击按钮,都不会出错了。

IIFE传递参数

IIFE是一个字符串表达式,如果需要传递参数,就是将参数合并到这个字符串表达式中。

例如:

'''Async
Dim
wv As WebViewer = e.Form.Controls("WebViewer1").WebViewer
'
传递参数给 IIFE
Dim
a As Integer = 150
Dim
b As Integer = 250
Dim
js As String = <![CDATA[
    (function(a, b) {
        const c = 100;
        return a + b + c;
    })(]]>
.Value & a & ", " & b & ");"
Dim sum As String = Await wv.ExecuteScriptAsync(js)
MessageBoxA.Show("计算结果" & sum)

如果你有困惑,可以用MessageBox显示一下合成的js代码,会看待到码为:

(function(a, b) {
    const c = 100;
    return a + b + c;
})(150, 250);

综合示例

普通用户请忽略这个示例。

本示例可参考示例文件"CaseStudy\WebViewer\调用Foxtable对象.Table"的窗口"可拖动HTML窗口js版"。

这个示例的AfterLoad事件代码如下,这里把窗口拖动功能封装成了一个IIFE表达式,粉红色的部分就是这个表达式。你可以直接复制这段代码到其他纯HTML窗口使用,因为IIFE表达式是闭包的,不用担心和你之前的代码有冲突。唯一要注意的是如果改了窗口标题元素的ID,代码也要做对应的修改,我用蓝色标记出来了:

'''Async
e.Form.BaseForm.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None
'去掉窗口边框
Dim
wv As WebViewer = e.Form.Controls("WebViewer1").WebViewer
Await
wv.EnsureCoreWebView2Async(Nothing)
Dim
ft As New FoxLib(wv) '创建Foxtable
wv.CoreWebView2.AddHostObjectToScript(
"ft", ft) '注入Foxtable
wv.CoreWebView2.AddHostObjectToScript("frm", e.Form) '单独导入窗口
Dim
html As String = <![CDATA[
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>
订单筛选窗口</title>
    <style>
        *{margin:0;padding:0;box-sizing:border-box;font-family:
微软雅黑;}
        html, body{height:100%;width:100%;background:#fff;}
        .filter-container{width:100%;height:100%;background:#fff;padding:20px;position: relative;}
        .filter-title{font-size:16px;font-weight:bold;margin-bottom:20px;color:#333;border-bottom:1px solid #eee;padding-bottom:10px;cursor: move;user-select: none;}
        .close-btn{position: absolute;top: 15px;right: 15px;width: 24px;height: 24px;border: none;background: transparent;cursor: pointer;padding: 0;z-index: 10;}
        .close-btn::before, .close-btn::after{content: '';position: absolute;width: 2px;height: 18px;background-color: #999;left: 11px;top: 3px;}
        .close-btn::before{transform: rotate(45deg);}
        .close-btn::after{transform: rotate(-45deg);}
        .close-btn:hover::before, .close-btn:hover::after{background-color: #dc3545;}
    </style>
</head>
<body>
    <div class="filter-container">
        <button class="close-btn" onclick="closeForm()" title="
关闭窗口"></button>
        <div class="filter-title" id="
dragHandle">
筛选</div>
        <!--
这里可以添加你的其他表单元素 -->
    </div>
     <script>
        const ft = window.chrome.webview.hostObjects.sync.ft;
        const frm = window.chrome.webview.hostObjects.sync.frm;
         //
拖动功能
       
(function() {
            const dragHandle = document.getElementById('
dragHandle');
            let isDragging = false;
            let lastX, lastY;  //
记录上一次鼠标位置      
            if (!dragHandle) return;         

            dragHandle.addEventListener('mousedown', function(e) {
                //
只有左键按下时才启用拖动
                if (e.button !== 0) return; 
                isDragging = true;               
                //
记录起始鼠标位置
                lastX = e.screenX;
                lastY = e.screenY;              
                e.preventDefault();
            });

            document.addEventListener('mousemove', function(e) {
                if (!isDragging) return; 
                try {
                    //
计算鼠标移动的距离
                    const currentX = e.screenX;
                    const currentY = e.screenY;
                    const deltaX = currentX - lastX;  //
水平移动距离
                    const deltaY = currentY - lastY;  //
垂直移动距离
                    //
如果有移动才调用
                    if (deltaX !== 0 || deltaY !== 0) {
                        //
移动窗口(相对距离)
                        frm.MoveAsync(deltaX, deltaY);                       
                        //
更新上一次位置
                        lastX = currentX;
                        lastY = currentY;
                    }                   
                    e.preventDefault();
                } catch(err) {
                    console.error('
移动窗口失败', err);
                    isDragging = false;
                }
            });           

            document.addEventListener('mouseup', function(e) {
                if (isDragging) {
                    isDragging = false;
                    e.preventDefault();
                }
            });           

            // 防止选中文本
            dragHandle.addEventListener('selectstart', function(e) {
                e.preventDefault();
            });
        })();

         function closeForm() {
            frm.CloseAsync();
        }
    </script>
</body>
</html>
]]>
.Value
wv.NavigateToString(html)


提示:Foxtale实现可拖动HTML窗口不用这么复杂,一行代码都不需要,参考:可拖动HTML窗口


本页地址:http://www.foxtable.com/webhelp/topics/6273.htm