WKWebView和js互调方法的实现

简介: WKWebView和js互调方法的实现

由于我最近比较忙,先把代码贴出来,以后再增加说明。

本文以加载本地h5页面方式进行演示,若是想把它变成加载网址的方式。只需要把[self loadWebHTMLSring:@“AWWKTest”];换成[self loadWebHTMLSring:url];就可以,当然url为h5的地址;也可以在跳转页面直接调用loadWebHTMLSring方法记载地址。

测试的本地h5文件AWWKTest.html代码,就是把这个文件加入工程中就可以。

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf8">
            <script language="javascript">
            //JS执行window.webkit.messageHandlers.onMenuShareTimeline.postMessage(<messageBody>)
            function onMenuShareTimelineClick() {
              window.webkit.messageHandlers.share.postMessage({title:'测试分享的标题',content:'测试分享的内容',url:'https://github.com/mayin1992'});
            }
            
            //分享回调结果显示
            function shareResult(channel_id,share_channel,share_url){
              var content = channel_id+","+share_channel+","+share_url;
              alert(content);
              document.getElementById("returnValue").value = content;
            }
        </script>
    </head>
    <body>
      <h1>这是按钮调用</h1>
      <input type="btton" value="分享" onclick="onMenuShareTimelineClick()" />
      <h1>回调展示区</h1>
      <textarea id ="returnValue" type="value" rows="5" cols="40">
      </textarea>
    </body>
</html>

AWWKWebViewController.h文件代码:

#import <UIKit/UIKit.h>

@interface AWWKWebViewController : UIViewController

/** 是否显示Nav */
@property (nonatomic,assign) BOOL isNavHidden;

@property(nonatomic,assign) BOOL isHaveTelLoginPage;
//@property (nonatomic, copy) void(^removeTelLoginPageCallBack)(void);

/**
 加载纯外部链接网页

 @param string URL地址
 */
- (void)loadWebURLSring:(NSString *)string;

/**
 加载本地网页
 
 @param string 本地HTML文件名
 */
- (void)loadWebHTMLSring:(NSString *)string;

/**
 加载外部链接POST请求(注意检查 XFWKJSPOST.html 文件是否存在 )
 postData请求块 注意格式:@"\"username\":\"xxxx\",\"password\":\"xxxx\""
 
 @param string 需要POST的URL地址
 @param postData post请求块 
 */
- (void)POSTWebURLSring:(NSString *)string postData:(NSString *)postData;
@end

AWWKWebViewController.m文件代码。这个是js调用oc的方法,oc直接调用js的方法并传参数,也可以oc处理了一些事情后或用户点击了oc的按钮后调用js的方法,自己根据自己的需要可以自己改造。

#import "AWWKWebViewController.h"

#import <WebKit/WKWebView.h>
#import <WebKit/WebKit.h>
#import "AWJsWebEntity.h"

typedef enum{
    loadWebURLString = 0,
    loadWebHTMLString,
    POSTWebURLString,
}wkWebLoadType;

static void *WkwebBrowserContext = &WkwebBrowserContext;

@interface AWWKWebViewController ()<WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler,UINavigationControllerDelegate,UINavigationBarDelegate>

@property (nonatomic, strong) WKWebView *wkWebView;
//设置加载进度条
@property (nonatomic,strong) UIProgressView *progressView;
//仅当第一次的时候加载本地JS
@property(nonatomic,assign) BOOL needLoadJSPOST;
//网页加载的类型
@property(nonatomic,assign) wkWebLoadType loadType;
//保存的网址链接
@property (nonatomic, copy) NSString *URLString;
//保存POST请求体
@property (nonatomic, copy) NSString *postData;;
@property (nonatomic,strong) UIButton *rightButton;
@property (nonatomic,strong) UIButton *leftBtn;
@property (nonatomic,strong) NSMutableString *cookieValue;
@end

@implementation AWWKWebViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self loadWebHTMLSring:@"AWWKTest"];
    //加载web页面
    [self webViewloadURLType];
    
    //添加到主控制器上
    [self.view addSubview:self.wkWebView];
    
    //添加进度条
    [self.view addSubview:self.progressView];
    
    UIBarButtonItem *barButton = [[UIBarButtonItem alloc]initWithCustomView:self.rightButton];
    self.navigationItem.rightBarButtonItem = barButton;
    UIBarButtonItem *leftBarButton = [[UIBarButtonItem alloc]initWithCustomView:self.leftBtn];
    self.navigationItem.leftBarButtonItem = leftBarButton;
    
    @weakify(self);
    [RACObserve(self.wkWebView.scrollView, contentSize) subscribeNext:^(id x) {
        @strongify(self);
        NSLog(@"xxxxxx:%@",x);
        [self updateNavigationItems];
    }];
    
}

-(UIButton*)leftBtn
{
    if (!_leftBtn)
    {
        _leftBtn =[[UIButton alloc]initWithFrame:CGRectMake(10, 20, 44, 44)];
        [_leftBtn setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal];
        [_leftBtn addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
        _leftBtn.hidden=NO;
    }
    return _leftBtn;
}

-(void)back
{
    [self goToLastView];
}

-(void)goToLastView
{
    if ([self.wkWebView canGoBack])
    {
        [self.wkWebView goBack];
    }
    else
    {
        WKBackForwardListItem *currentItem = self.wkWebView.backForwardList.currentItem;
        NSString *url = [currentItem.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        if([url rangeOfString:telLoginHtml].location != NSNotFound)
        {
            [self.navigationController popViewControllerAnimated:YES];
        }
        else
        {
           [[AWNoticeView currentNotice] showErrorNotice:@"你已经在主页,不能后退了"];
        }
        //        [self.navigationController popViewControllerAnimated:YES];
    }
}

-(UIButton*)rightButton
{
    if (!_rightButton)
    {
        _rightButton =[[UIButton alloc]initWithFrame:CGRectMake(FULL_WIDTH - 10 - 44, 20, 44, 44)];
        [_rightButton setImage:[UIImage imageNamed:@"share"] forState:UIControlStateNormal];
        [_rightButton addTarget:self action:@selector(roadLoadClicked) forControlEvents:UIControlEventTouchUpInside];
        _rightButton.hidden=NO;
    }
    return _rightButton;
}

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
//    if (self.removeTelLoginPageCallBack)
//    {
//        self.removeTelLoginPageCallBack();
//    }
    if (_isNavHidden == YES) {
        self.navigationController.navigationBarHidden = YES;
        //创建一个高20的假状态栏
        UIView *statusBarView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 20)];
        //设置成绿色
        statusBarView.backgroundColor=[UIColor whiteColor];
        // 添加到 navigationBar 上
        [self.view addSubview:statusBarView];
    }else{
        self.navigationController.navigationBarHidden = NO;
    }
}


- (void)roadLoadClicked{
    [self.wkWebView reload];
}

-(void)customBackItemClicked{
    if (self.wkWebView.goBack) {
        [self.wkWebView goBack];
    }else{
        [self.navigationController popViewControllerAnimated:YES];
    }
}
-(void)closeItemClicked{
    [self.navigationController popViewControllerAnimated:YES];
}

#pragma mark ================ 加载方式 ================

- (void)webViewloadURLType{
    switch (self.loadType) {
        case loadWebURLString:{
            //创建一个NSURLRequest 的对象
//            NSURLRequest * Request_zsj = [NSURLRequest requestWithURL:[NSURL URLWithString:self.URLString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
//            //加载网页
//            [self.wkWebView loadRequest:Request_zsj];
            
            NSMutableURLRequest * Request_zsj = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.URLString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
//            if(self.cookieValue)
//            {
//                [Request_zsj addValue:self.cookieValue forHTTPHeaderField:@"Cookie"];
//            }
            
            //加载网页
            [self.wkWebView loadRequest:Request_zsj];
            
            break;
        }
        case loadWebHTMLString:{
            [self loadHostPathURL:self.URLString];
            break;
        }
        case POSTWebURLString:{
            // JS发送POST的Flag,为真的时候会调用JS的POST方法
            self.needLoadJSPOST = YES;
            //POST使用预先加载本地JS方法的html实现,请确认WKJSPOST存在
            [self loadHostPathURL:@"AWWKJSPOST.htm"];
            break;
        }
    }
}

- (void)loadHostPathURL:(NSString *)url{
    //获取JS所在的路径
    NSString *path = [[NSBundle mainBundle] pathForResource:url ofType:@"html"];
    //获得html内容
    NSString *html = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    //加载js
    [self.wkWebView loadHTMLString:html baseURL:[[NSBundle mainBundle] bundleURL]];
}

// 调用JS发送POST请求
- (void)postRequestWithJS {
    // 拼装成调用JavaScript的字符串
    NSString *jscript = [NSString stringWithFormat:@"post('%@',{%@});", self.URLString, self.postData];
    // 调用JS代码
    [self.wkWebView evaluateJavaScript:jscript completionHandler:^(id object, NSError * _Nullable error) {
    }];
}


- (void)loadWebURLSring:(NSString *)string{
    self.URLString = string;
    self.loadType = loadWebURLString;
}

- (void)loadWebHTMLSring:(NSString *)string{
    self.URLString = string;
    self.loadType = loadWebHTMLString;
}

- (void)POSTWebURLSring:(NSString *)string postData:(NSString *)postData{
    self.URLString = string;
    self.postData = postData;
    self.loadType = POSTWebURLString;
}

#pragma mark ================ 自定义返回/关闭按钮 ================

-(void)updateNavigationItems{
    WKBackForwardListItem *currentItem = self.wkWebView.backForwardList.currentItem;
    NSString *url = [currentItem.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSLog(@"current url:%@", url);
    self.navigationItem.leftBarButtonItem.customView.hidden = [AWGeneralFunction isHiddenWebBackButton:url];
}

#pragma mark ================ WKNavigationDelegate ================

//这个是网页加载完成,导航的变化
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    /*
     主意:这个方法是当网页的内容全部显示(网页内的所有图片必须都正常显示)的时候调用(不是出现的时候就调用),,否则不显示,或则部分显示时这个方法就不调用。
     */
    // 判断是否需要加载(仅在第一次加载)
    if (self.needLoadJSPOST) {
        // 调用使用JS发送POST请求的方法
        [self postRequestWithJS];
        // 将Flag置为NO(后面就不需要加载了)
        self.needLoadJSPOST = NO;
    }
    // 获取加载网页的标题
    self.title = self.wkWebView.title;
    
//    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    [self updateNavigationItems];
//    NSArray*backList = self.wkWebView.backForwardList.backList;
//    NSLog(@"backList:%@, backList.count = %lld", backList, backList.count);
}

//开始加载
-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
    //开始加载的时候,让加载进度条显示
    self.progressView.hidden = NO;
}

//内容返回时调用
-(void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{}

//服务器请求跳转的时候调用
-(void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{}

//服务器开始请求的时候调用
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSString *strRequest = [navigationAction.request.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSLog(@"isHaveTelLoginPage:%d, strRequest:%@",[AWSingleObject sharedInstance].isHaveTelLoginPage, strRequest);
    if(isEmptyString(strRequest))
    {
        decisionHandler(WKNavigationActionPolicyCancel);//不允许跳转
        return;
    }
    else if([strRequest isEqualToString:@"about:blank"]) {//主页面加载内容
        decisionHandler(WKNavigationActionPolicyAllow);//允许跳转
    }

    switch (navigationAction.navigationType) {
        case WKNavigationTypeLinkActivated: {
            break;
        }
        case WKNavigationTypeFormSubmitted: {
            break;
        }
        case WKNavigationTypeBackForward: {
            break;
        }
        case WKNavigationTypeReload: {
            break;
        }
        case WKNavigationTypeFormResubmitted: {
            break;
        }
        case WKNavigationTypeOther: {
            break;
        }
        default: {
            break;
        }
    }
    [self updateNavigationItems];
    decisionHandler(WKNavigationActionPolicyAllow);
}

// 内容加载失败时候调用
-(void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error{
    NSLog(@"页面加载超时");
}

//跳转失败的时候调用
-(void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{}

//进度条
-(void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{}

#pragma mark ================ WKUIDelegate ================

// 获取js 里面的提示
-(void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:message preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }]];
    
    [self presentViewController:alert animated:YES completion:NULL];
}


//KVO监听进度条
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    
    if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))] && object == self.wkWebView) {
        [self.progressView setAlpha:1.0f];
        BOOL animated = self.wkWebView.estimatedProgress > self.progressView.progress;
        [self.progressView setProgress:self.wkWebView.estimatedProgress animated:animated];
        
        // Once complete, fade out UIProgressView
        if(self.wkWebView.estimatedProgress >= 1.0f) {
            [UIView animateWithDuration:0.3f delay:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^{
                [self.progressView setAlpha:0.0f];
            } completion:^(BOOL finished) {
                [self.progressView setProgress:0.0f animated:NO];
            }];
        }
    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

#pragma mark ================ WKScriptMessageHandler ================

//拦截执行网页中的JS方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{

    //服务器固定格式写法 window.webkit.messageHandlers.名字.postMessage(内容);
    //客户端写法 message.name isEqualToString:@"名字"]
    if ([message.name isEqualToString:@"share"]) {
        NSLog(@"%@", message.body);
        
        //oc调用js的shareResult方法并且传递参数
        [self.wkWebView evaluateJavaScript:@"shareResult('dd', 'aa', '56');" completionHandler:^(id _Nullable data, NSError * _Nullable error) {
            
            
        }];
    }
}

#pragma mark ================ 懒加载 ================

- (WKWebView *)wkWebView{
    if (!_wkWebView) {
        //设置网页的配置文件
        WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc]init];

        //允许视频播放
        if (@available(iOS 9.0, *)) {
            configuration.allowsAirPlayForMediaPlayback = YES;
        } else {
            // Fallback on earlier versions
        }
        // 允许在线播放
        configuration.allowsInlineMediaPlayback = YES;
        // 允许可以与网页交互,选择视图
        configuration.selectionGranularity = YES;
        // web内容处理池
        configuration.processPool = [[WKProcessPool alloc] init];
        
        //自定义配置,一般用于 js调用oc方法(OC拦截URL中的数据做自定义操作)
        WKUserContentController * userContentController = [[WKUserContentController alloc]init];
        // 添加消息处理,注意:self指代的对象需要遵守WKScriptMessageHandler协议,结束时需要移除
        [userContentController addScriptMessageHandler:self name:@"share"];
        WKPreferences *preferences = [WKPreferences new];
        preferences.javaScriptCanOpenWindowsAutomatically = YES;
        configuration.preferences = preferences;
        
        // 是否支持记忆读取
        configuration.suppressesIncrementalRendering = YES;
        // 允许用户更改网页的设置
        configuration.userContentController = userContentController;
        _wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
        _wkWebView.backgroundColor = [UIColor whiteColor];//[UIColor colorWithRed:240.0/255 green:240.0/255 blue:240.0/255 alpha:1.0];
        // 设置代理
        _wkWebView.navigationDelegate = self;
        _wkWebView.UIDelegate = self;
        //kvo 添加进度监控
        [_wkWebView addObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress)) options:0 context:WkwebBrowserContext];
        //开启手势触摸
        _wkWebView.allowsBackForwardNavigationGestures = YES;
        // 设置 可以前进 和 后退
        //适应你设定的尺寸
        [_wkWebView sizeToFit];
    }
    return _wkWebView;
}

- (UIProgressView *)progressView{
    if (!_progressView) {
        _progressView = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleDefault];
        if (_isNavHidden == YES) {
            _progressView.frame = CGRectMake(0, 20, self.view.bounds.size.width, 3);
        }else{
            _progressView.frame = CGRectMake(0, 64, self.view.bounds.size.width, 3);
        }
        // 设置进度条的色彩
        [_progressView setTrackTintColor:[UIColor colorWithRed:240.0/255 green:240.0/255 blue:240.0/255 alpha:1.0]];
        _progressView.progressTintColor = [UIColor colorWithHexString:@"0X057dff"];//[UIColor greenColor];
    }
    return _progressView;
}

-(void)viewWillDisappear:(BOOL)animated{
    [self.wkWebView.configuration.userContentController removeScriptMessageHandlerForName:@"share"];
    [self.wkWebView setNavigationDelegate:nil];
    [self.wkWebView setUIDelegate:nil];
}

//注意,观察的移除
-(void)dealloc{
    [self.wkWebView removeObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
}

@end

iOS调用js方法:

-(void)sendJsSuccessMessageWithParams:(NSMutableDictionary *)params
{
    if(isCommonUnitEmptyDict(params))
    {
        return;
    }
    NSString *callBackStr = [NSString stringWithFormat:@"success(%@);", [params getJsonString]];
    [self callBackWithCallBackStr:callBackStr];//注意:函数里面这个要是去除换行符的json字符串
}
-(void)sendJsRechargeFailedMessage
{
    NSString *callBackStr = [NSString stringWithFormat:@"failed();"];
    [self callBackWithCallBackStr:callBackStr];
}

-(void)callBackWithCallBackStr:(NSString *)callBackStr
{
    NSLog(@"callBackStr :%@", callBackStr);
    if(isCommonUnitEmptyString(callBackStr))
    {
        return;
    }
    bg_dispatch_main_async_safe(^{
        //oc调用js的shareResult方法并且传递参数
        [self.wkWebView evaluateJavaScript:callBackStr completionHandler:^(id _Nullable data, NSError * _Nullable error) {
            
            
        }];
    });
}

这个是具体调用例子:

            NSMutableDictionary *params = [NSMutableDictionary dictionary];
            [params setValue:@"0.01" forKey:@"amount"];
            [params setValue:@"cardNameValue" forKey:@"cardName"];

            [self sendJsSuccessMessageWithParams:params];

由于最近项目正做根据微信端直接产生原生app,并保存微信app所有页面。踩了很多坑,特别是WKWebView的坑不是一般的深。我有其它文章解决:WKWebView和js互调方法参数传递及注意事项见文章《向js发送含有NSDictionary对象或NSArray对象的消息》。完美解决通过TZImagePickerController组件获取的多选图片对象及PHAsset数组,通过PHAsset直接获取到的图像被旋转问题及图片名称的获取见文章《通过PHAsset获取的图片上传后变大和图像被旋转90度问题完美解决方案》。WKWebView对图片地址的拦截及获取本地图片并显示问题见文章《WKWebView采用HybridNSURLProtocol协议拦截图片等资源预加载》。

目录
相关文章
|
5天前
|
Web App开发 JavaScript 前端开发
如何确保 Math 对象的方法在不同的 JavaScript 环境中具有一致的精度?
【10月更文挑战第29天】通过遵循标准和最佳实践、采用固定精度计算、进行全面的测试与验证、避免隐式类型转换以及持续关注和更新等方法,可以在很大程度上确保Math对象的方法在不同的JavaScript环境中具有一致的精度,从而提高代码的可靠性和可移植性。
|
18天前
|
缓存 监控 前端开发
JavaScript 实现大文件上传的方法
【10月更文挑战第17天】通过以上步骤和方法,我们可以实现较为可靠和高效的大文件上传功能。当然,具体的实现方式还需要根据实际的应用场景和服务器要求进行调整和优化。
|
5天前
|
JavaScript 前端开发 索引
js中DOM的基础方法
【10月更文挑战第31天】这些DOM基础方法是操作网页文档结构和实现交互效果的重要工具,通过它们可以动态地改变页面的内容、样式和行为,为用户提供丰富的交互体验。
|
5天前
|
缓存 JavaScript UED
js中BOM中的方法
【10月更文挑战第31天】
|
4天前
|
JavaScript 前端开发
.js方法参数argument
【10月更文挑战第26天】`arguments` 对象为JavaScript函数提供了一种灵活处理参数的方式,能够满足各种不同的参数传递和处理需求,在实际开发中具有广泛的应用价值。
19 7
|
5天前
|
JavaScript 前端开发 图形学
JavaScript 中 Math 对象常用方法
【10月更文挑战第29天】JavaScript中的Math对象提供了丰富多样的数学方法,涵盖了基本数学运算、幂运算、开方、随机数生成、极值获取以及三角函数等多个方面,为各种数学相关的计算和处理提供了强大的支持,是JavaScript编程中不可或缺的一部分。
|
10天前
|
JavaScript 前端开发 Go
异步加载 JS 的方法
【10月更文挑战第24天】异步加载 JavaScript 是提高网页性能和用户体验的重要手段。通过使用不同的方法和技术,可以实现灵活、高效的异步加载 JavaScript。在实际应用中,需要根据具体情况选择合适的方法,并注意处理可能出现的问题,以确保网页能够正常加载和执行。
|
22天前
|
人工智能 JavaScript 网络安全
ToB项目身份认证AD集成(三完):利用ldap.js实现与windows AD对接实现用户搜索、认证、密码修改等功能 - 以及针对中文转义问题的补丁方法
本文详细介绍了如何使用 `ldapjs` 库在 Node.js 中实现与 Windows AD 的交互,包括用户搜索、身份验证、密码修改和重置等功能。通过创建 `LdapService` 类,提供了与 AD 服务器通信的完整解决方案,同时解决了中文字段在 LDAP 操作中被转义的问题。
|
23天前
|
存储 JavaScript 前端开发
JavaScript 数据类型详解:基本类型与引用类型的区别及其检测方法
JavaScript 数据类型分为基本数据类型和引用数据类型。基本数据类型(如 string、number 等)具有不可变性,按值访问,存储在栈内存中。引用数据类型(如 Object、Array 等)存储在堆内存中,按引用访问,值是可变的。本文深入探讨了这两种数据类型的特性、存储方式、以及检测数据类型的两种常用方法——typeof 和 instanceof,帮助开发者更好地理解 JavaScript 内存模型和类型检测机制。
53 0
JavaScript 数据类型详解:基本类型与引用类型的区别及其检测方法
|
28天前
|
JavaScript 前端开发 测试技术
JS都有哪些操作数组的方法
JS都有哪些操作数组的方法
20 3